hdoj 4906 状态dp

来源:互联网 发布:angularjs js cdn 编辑:程序博客网 时间:2024/04/28 07:27

dp[i]用二进制表写出后第i位代表的是i是否能够得到(0<=i<20),
所以我们只需要一个2^20的dp数组循环n次即可。在每次循环中,
我们枚举这位填的是j,i能转移到i + 1<<(j-1)(j本身) + ((i<<j)&maxn)(已经能得到的结果全部加上j,
同时这些数需要在0-k的范围中),即组成了所有加上j之后的情况。
同时每个dp[i]都需要加上dp[i]*extra(extra代表的是l中大于k的数)
最后我们只需要检索所有在1<<k位的dp值的和即可

代码如下:

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;__int64 mod=1e9+7;__int64 dp[(1<<20)+100];int T,n,k;__int64 L,all,ans;void work(){    memset(dp,0,sizeof(dp));    int i,j,t,Min;    __int64 p=0,next;    Min=k;ans=0;    if(L>k) p=L-k;    else Min=L;    all=(1<<k)-1;    dp[0]=1;    for(i=0;i<n;i++)    {        for(j=all;j>=0;j--)        {            if(dp[j]==0) continue;            __int64 num=dp[j];            for(t=1;t<=Min;t++)            {                next=(1<<(t-1))|j|((j<<t)&all);                //加上t本身这个数 + 原来可取到的数 + 原来可取到的数加上t之后的数,即组成了所有加上t之后的情况                dp[next]+=num;                dp[next]%=mod;            }            dp[j]=(dp[j]+num*p)%mod;  //第i个数选大于k的数        }    }    for(i=1;i<=all;i++)    {        if(i&(1<<(k-1))) //k能取到的情况全加        {            ans+=dp[i];            ans%=mod;        }    }    printf("%I64d\n",ans);}int main(){    int i,j;    scanf("%d",&T);    while(T)    {        scanf("%d%d%I64d",&n,&k,&L);        work();        T--;    }    return 0;}


 

0 0
原创粉丝点击