Arrange the Bulls POJ - 2441
考察:状压dp
POJ 3254的延伸题
思路:
讲牛的需求看成看成肥沃的土地,也就是把谷仓看成一个矩阵.与牛的需求相符的位置可以养牛.这样就变成了POJ 3254差不多的题.f[i,j]表示前i头牛谷仓的养殖情况.状态转移方程是f[i][j] += f[i-1][k].
这道题直接枚举i、j、k有超时的危险,我们可以把j、k一个换成m,另一个则是1<<m.这两个哪个循环在外面也可以优化.
注意:用滚动数组要清零.j循环在内方便数组清零
再再注意:这道题的f[i-1&1][j]保证了是need[i-1]允许的情况,而下面的!(j>>k&1)保证上下两行不会在同一列.(need[i]>>k&1)保证了算的情况是need[i]符合的.
1 #include <iostream> 2 #include <cstring> 3 #include <algorithm> 4 #include <cstdio> 5 using namespace std; 6 const int N = 20; 7 int need[N],f[2][1<<N]; 8 int main() 9 { 10 int n,m,ans = 0; 11 scanf("%d%d",&n,&m); 12 for(int i=0;i<n;i++) 13 { 14 int p; scanf("%d",&p); 15 for(int j=1;j<=p;j++) 16 { 17 int x; scanf("%d",&x); 18 need[i]|=(1<<(x-1)); 19 } 20 } 21 for(int i=0;i<m;i++) if(need[0]>>i&1) f[0&1][1<<i] = 1; 22 for(int i=1;i<n;i++) 23 for(int j=0;j<1<<m;j++) 24 if(f[i-1&1][j])//保证是i-1有的 25 { 26 for(int k=0;k<m;k++) 27 if(!(j>>k&1)&&(need[i]>>k&1))//保证是need[i]没有的 28 f[i&1][j|(1<<k)] += f[i-1&1][j]; 29 f[i-1&1][j] = 0; 30 } 31 for(int i=1;i<1<<m;i++) ans+=f[n-1&1][i]; 32 printf("%d\n",ans); 33 return 0; 34 }
2021.3.16 二刷 写了个压缩为1维的做法,类似01背包的压缩
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <vector> 5 using namespace std; 6 const int N = 21,M = 20; 7 int n,m,mp[N],f[1<<M],ans; 8 int main() 9 { 10 scanf("%d%d",&n,&m); 11 for(int i=1;i<=n;i++) 12 { 13 int x; 14 scanf("%d",&x); 15 while(x--) 16 { 17 int a; scanf("%d",&a); 18 mp[i]|=1<<(a-1); 19 } 20 } 21 f[0] = 1; 22 for(int i=1;i<=n;i++) 23 for(int j=1<<m;j>=0;j--) 24 { 25 if(f[j]) 26 for(int k=0;k<m;k++) 27 if(!(j>>k&1)&&(mp[i]>>k&1)) 28 { 29 f[j|(1<<k)] += f[j]; 30 } 31 f[j] = 0; 32 } 33 for(int i=0;i<1<<m;i++) ans+=f[i]; 34 printf("%d\n",ans); 35 return 0; 36 }