首页 > 代码库 > hdu 4906 状压dp

hdu 4906 状压dp

/*ID: neverchanjePROG:LANG: C++11*/#include<vector>#include<iostream>#include<cstring>#include<string>#include<algorithm>#include<cmath>#include<cstdio>#include<set>#include<queue>#include<map>using namespace std;#define INF 1e9#define maxn#define MOD 1000000007#define rep(i,x,y) for(int i=x;i<=y;i++)#define mset(x) memset(x,0,sizeof(x))typedef long long ll;typedef pair<int,int> pii;typedef vector<int> vi;ll dp[1<<(21)] ,t,n,k,l,d, next, maxS;int main(){//    freopen("a.txt","r",stdin);//    freopen(".out","w",stdout);    cin>>t;    while(t--){        cin>>n>>k>>l;        mset(dp);        d = l>k ? l-k : k-l;        l = min(k,l);        maxS = (1<<k)-1;        dp[0]=1; //dp[0][0]=1;        int tmp;        rep(i,1,n){            for(int S=maxS; S>=0; S--){                if(dp[S]==0)    continue; //重要优化                tmp = dp[S];//必须保存,因为next可能等于S                rep(p,1,l){                    next = ( S | (1<<(p-1)) | ((S<<p)&maxS) );                    dp[ next ] = ( tmp+dp[next] )%MOD;                }                dp[S] = ( dp[S] + (ll)(tmp*d)%MOD )%MOD;            }        }        ll ans=0;        rep(S,0,maxS){            if( S&(1<<(k-1)) ){                ans += dp[S];                ans %= MOD;            }        }        cout<<ans<<endl;    }    return 0;}/*DESCRIPTION:枚举n个数的数列a,有ai<=L,如果存在子集使得子集的数之和为k,则a是一个好数列注意到k<=20假设a的不同子集和分别是s1,s2,...,st,记做集合S枚举第i个数p (1 ~ min(l,k))每个子集和si+p 会得到新的子集和的集合S‘dp[i][S+{p}+S‘] += dp[i-1][S];(S<<p) = S‘    //每个si都加pS+{p}+S‘ = S | (1<<(p-1)) | ((S<<p)&maxS)边界:dp[0]=1;dp的大小maxS为(1<<k)-1注意情况:还要再枚举p (min(l,k) ~ max(l,k)),显然这个p不会被选,但也算作一种情况dp[i][S] += dp[i-1][S]*(l-k)*/