标签:name wing paper 通过 mina sed material seve numbers
Input
1 2 1 3 1 4 2 2 2 3 2 4 2 11 4 11 0 0Sample Output
1 0 1 2 3 5 144 51205
(一句话题意)
本题的数据范围很小,nm均为11,这提示我们可以用状压。我们可以预处理出所有横放骨牌的状态,即表示为一个二进制串,如果为1,则证明这里有骨牌,为0,则证明这里没有骨牌。
1 bool check(int x) 2 { 3 bool ok=1; 4 for(int i=0;i<n;i++) 5 { 6 if(x&(1<<i)) ok=ok^1; 7 //当前位为1,记下为1的个数,最终必须两两相邻且个数为偶数 8 if((!(x&(1<<i)))&&!ok) return 0; 9 } 10 if(ok) return 1; 11 else return 0; 12 }
我们考虑设计一个状态,f[i][j]表示当前在第i行,当前行状态为j的方案数,我们的转移目标是f[m][(1<<n)-1](即在最后一行,每个地方都被填满)
则可以写出转移方程:f[i][j]=+f[i-1][bin[k]^j^fAKe](k枚举上一行的状态,满足条件k是j的子集,也就是能转移到当前状态的上一状态)两次异或是在求能转移到当前状态的上一状态。按学长的话说就是:那么我们对于每个状态,如果我们知道了它哪些是横放的,那么我们也就可以推出它上一行的状态,因为当前行必须要把上一行补满。
注意细节:开long long(WA了好几次)
code
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 5 using namespace std; 6 typedef long long ll; 7 8 int n,m,cnt; 9 int bin[3000]; 10 ll f[12][2050]; 11 12 bool check(int x) 13 { 14 bool ok=1; 15 for(int i=0;i<n;i++) 16 { 17 if(x&(1<<i)) ok=ok^1; 18 if((!(x&(1<<i)))&&!ok) return 0; 19 } 20 if(ok) return 1; 21 else return 0; 22 } 23 24 void init() 25 { 26 memset(f,0,sizeof(f)); 27 memset(bin,0,sizeof(bin)); 28 cnt=0; 29 } 30 31 int main() 32 { 33 34 while(scanf("%d%d",&n,&m)) 35 { 36 if(n==0) break; 37 int fAKe=(1<<n)-1; 38 for(int i=0;i<=fAKe;i++) 39 if(check(i)) bin[++cnt]=i; 40 for(int i=1;i<=cnt;i++) f[1][bin[i]]=1; 41 for(int i=2;i<=m;i++) 42 for(int j=0;j<=fAKe;j++) 43 for(int k=1;k<=cnt;k++) 44 if((bin[k]|j)==j) f[i][j]+=f[i-1][bin[k]^j^fAKe]; 45 printf("%lld\n",f[m][fAKe]); 46 init(); 47 } 48 return 0; 49 }
注:我们这里说的子集概念,举个例子:
{1,3,4}为一集合,则二进制第1位或第三位或第4位上为1的所有数称为它的子集。由于或运算满足“有一个1即为1”,则我们通过或运算可以来检查是否存在子集关系。
POJ 3411 Mondriaan's Dream 【状压Dp】 By cellur925
标签:name wing paper 通过 mina sed material seve numbers
原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9445523.html