poj 2411 Mondriaan's Dream 状态压缩dp
邓姐推荐做这道题,果然是一道不错的题目,据说是插头dp,不过我不会哦,咋办呢,这种题只好暴力乱搞了
一开始用3进制来存储状态 0代表横放 1代表竖着放朝上 2代表竖着放朝下 然后暴力搜了一下 复杂度3^22 果断超时了
后来想了一下,其实上一行的每个格子状态都搜完了之后,只要不是朝下 那么就对下一行没有影响了 于是更改了一下状态存储方式
0代表这个格子放的方式不会影响下一行 1代表会影响下一行
容易证明 不冲突的两行 下一行有且只有一种合法的方式
所以复杂度就从 3^22缩减到了2^22 并且二进制存储与位运算 速度飞快 就过了
具体的就看代码咯
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Source Code Problem: 2411 User: tangrui Memory: 416K Time: 47MS Language: C++ Result: Accepted Source Code #include<iostream> #include<cmath> #include<vector> #include<cstdio> #include<cstring> using namespace std; int n,m; long long t[(1<<11)+10][11]; int x; // 0代表仅占自己这行 1代表竖着放占了下一行 int selfcheck(int a) { int odd=0; //相邻的两行同样位置都是0代表这个位置是横着放的 因此必须是偶数个 for(int k=a,i=0;i<n;i++) { if(!(k&1))odd=!odd; else if(odd) return 0; k>>=1; } if(odd) return 0; return 1; } int correct(int a,int b) { if(a&b)return 0; //当相邻的两行有同样位置的两个1时一定是冲突的 return selfcheck(a|b); } long long dp(int now,int k) { if(k==m-1) { return correct(0,now); //因为最后一行状态只能是0 所以倒数第二行就判断 } if(t[now][k])return t[now][k]; //你懂的 for(int i=0;i<x;i++) { if(correct(i, now)) t[now][k]+=dp(i,k+1); } return t[now][k]; } int main() { while(cin>>n>>m&&(n|m)) { if(n==0||m==0||n*m%2==1) { cout<<0<<endl; continue; } memset(t,0,sizeof(t)); if(n>m) swap(n,m); //为了时间 尽量让n小些 x=1<<n; //每一行的总状态数为 2^n cout<<dp(0,0)<<endl; } }