您的位置:首页 > 其它

hdu 4906 状压dp

2016-05-31 00:03 363 查看
题意:给出n,k<=20,L<=10^9 问能不能通过不大于L的数组成一个含n个数的序列使得能从这个序列中挑出几个数的和为k,求满足条件序列的个数

题解:看到k<20可以用sum的状态做状压dp的状态

状态转移方程next=(1<<(p-1))|j|((j<<p)&SIZE); dp[next]=(dp[next]+v)%mod;

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll __int64
using namespace std;
const ll mod=1e9+7;
ll dp[1<<21],v;
int main()
{
int T,n,k,l,MIN,SIZE,i,j,p;
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&n,&k,&l);
memset(dp,0,sizeof(dp));
dp[0]=1;//赋初值不然没法启动
SIZE=(1<<k)-1;//最大状态
ll MIN=min(l,k);//因为大于k的值是没用的可以在后面直接加上去
for(i=1;i<=n;i++){//因为要加n个数这里枚举次数
for(j=SIZE;j>=0;j--){//倒着枚举状态是类似背包一样防止重复加
v=dp[j];
if(v==0)continue;
for(p=1;p<=MIN;p++){
int next=(1<<(p-1))|j|((j<<p)&SIZE);//加上数p后的状态1.p本身2.j本身3.j的每一位加上p后的状态
dp[next]=(dp[next]+v)%mod;
}
if(k<l)dp[j]=(dp[j]+(ll)v*(l-k))%mod;//剩下部分直接加上去不知道为什么看有些blog没限制条件答案也是对的
}//是数据太弱吗
}
ll ans=0;
for(i=0;i<=SIZE;i++)
if(i&(1<<(k-1))) (ans+=dp[i])%=mod;
printf("%I64d\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: