状态压缩
P1896 [SCOI2005] 互不侵犯
https://www.luogu.com.cn/problem/P1896
这个状态枚举需要注意的是枚举行会直接枚举到n+1行,因为n+1行的dp[n+1][k][0]
能和上一层所有符合条件的dp[n][k][a]
兼容,所以就相当于直接把上一层的结果直接集成起来了,不过不这样做的话直接对最后一层的所有可能情况求和也是可以的,如果想要尝试这一种方法的话就看第二段代码(这个行遍历只到第n行)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| #include<bits/stdc++.h> using namespace std; using u32 = unsigned; using ll = long long; using u64 = unsigned long long; const int inf = 2147483647; const ll INF = 1e18; ll dp[12][144][1<<12];
int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n,k; cin>>n>>k; vector<int>s(1<<n); vector<int>num(1<<n); int cnt=0; for(int i=0;i<(1<<n);i++) { if(!(i&i>>1)) { s[cnt++]=i; for(int j=0;j<n;j++) { if(i>>j&1) { num[i]++; } } } } dp[0][0][0]=1; for(int i=1;i<=n+1;i++) { for(int j=0;j<=k;j++) { for(int a=0;a<cnt;a++) { for(int b=0;b<cnt;b++) { int c=num[s[a]]; if(j<c||s[a]&s[b]||s[a]&(s[b]>>1)||s[a]&(s[b]<<1))continue; dp[i][j][a]+=dp[i-1][j-c][b]; } } } } cout<<dp[n+1][k][0]<<'\n'; return 0; }
|
1 2 3 4 5 6
| ll sum=0; for(int i=0;i<cnt;i++) { sum+=dp[n][k][i]; } cout<<sum<<'\n';
|
P1879 [USACO06NOV] Corn Fields G
https://www.luogu.com.cn/problem/P1879
玉米田这一道题整体上来说是比小国王简单的,但是多了一点需要处理行内土地是否肥沃这一种情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| #include<bits/stdc++.h> using namespace std; using u32 = unsigned; #define i128 __int128; using ll = long long;
using u64 = unsigned long long; const int inf = 2147483647; const ll INF = 1e18; int g[14]; int s[1<<14]; int dp[14][1<<14]; const int mod=1e8; signed main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n,m; cin>>n>>m; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { int x; cin>>x; g[i]=(g[i]<<1)+x; } } int cnt=0; for(int i=0;i<(1<<m);i++) { if(!(i&(i>>1))) { s[cnt++]=i; } } dp[0][0]=1; for(int i=1;i<=n+1;i++) { for(int a=0;a<cnt;a++) { for(int b=0;b<cnt;b++) { if((s[a]&g[i])!=s[a]||s[a]&s[b])continue; dp[i][a]=(dp[i][a]+dp[i-1][b])%mod; } } } cout<<dp[n+1][0]<<'\n';
return 0; }
|
P2704 [NOI2001] 炮兵阵地
https://www.luogu.com.cn/problem/P2704
P10975 Mondriaan’s Dream (蒙德里安的梦想)
https://www.luogu.com.cn/problem/P10975
如果该位置放的是一行两列的矩形,那么第一个位置是1,第二个位置是0。如果放的是两行一列的矩形,那么两个位置都是0。然后对每一列的状态进行状态压缩。判断相邻的两列是否兼容:两列进行|(或运算),如果某一位是0,说明这两列之一位置都是0,都不能是横放状态下的第一个,之后如果存在连续0的个数为奇数,竖放也放不下,所以不兼容,同时如果两列某一位置同时为1,也不能兼容。先创建一个vis数组处理两行|之后的结果是否兼容,然后再判断是否某一位同时为1。最后一列一列传递兼容的值,到最后一列只能全部为0,也就是状态0。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| #include<bits/stdc++.h> using namespace std; using u32 = unsigned; #define i128 __int128; using ll = long long;
using u64 = unsigned long long; const int inf = 2147483647; const ll INF = 1e18; const int N=12,M=1<<N; int vis[M]; signed main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n,m; while(cin>>n>>m,n||m) { vector<vector<ll>>dp(m+1,vector<ll>(1<<n+1)); for(int i=0;i<(1<<n);i++) { int cnt=0; vis[i]=true; for(int j=0;j<n;j++) { if(i>>j&1) { if(cnt&1){vis[i]=false;break;} } else cnt++; } if(cnt&1)vis[i]=false; } dp[0][0]=1; for(int i=1;i<=m;i++) { for(int j=0;j<(1<<n);j++) { for(int k=0;k<(1<<n);k++) { if((j&k)==0&&vis[j|k]) { dp[i][j]+=dp[i-1][k]; } } } } cout<<dp[m][0]<<'\n'; }
return 0; }
|