Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 12395 | Accepted: 7233 |
Description
Input
Output
Sample Input
1 2 1 3 1 4 2 2 2 3 2 4 2 11 4 11 0 0
Sample Output
1 0 1 2 3 5 144 51205
经典的棋盘覆盖问题。横放看做(11)竖放看做(01),然后按行状压dp。
未优化题解:题解
优化题解:题解
优化是用了dfs去找出第i-1行状态s与第i行哪些状态scur相匹配,而并不是盲目枚举所有状态。这题要理解dfs是有些困难的,再发一篇题解:题解 ,这份题解里的dfs和上面优化代码是类似的,dfs里都考虑了3种情况,
情况一:第i-1行第j列为0,那么第i行第j列只能为1
情况二:第i-1行第j列为1,第i行第j列为0
那么第三种情况有人可能会认为是:第i-1行第j列为1,第i行第j列为1,显然并不是这么简单,比如这种情况
0 1
1 1
这个最后一列1是和1搭配,但是前一列显然对应了竖着放,而最后一列不可能是竖着放,只可能是最后一行横着放,那么就矛盾了,因此如果简单的<第i-1行第j列为1,第i行第j列为1>,dfs往下就会出错,因此可以发现,第三种情况应为:第i-1行第j-1,j列都为1,第i行第j-1,j列都为1。至于第二种情况,你可能怀疑会不会也出现类似第三种情况这种矛盾呢?但是枚举状态后是不会发生的,因此也就是这三种情况了。
优化代码基本还是在状压dp,dfs是对一个特定的状态s进行匹配,而我发的最后一份题解,则换了个思路,是用dfs将所有可能的两行状态全部枚举出来,然后就相当于有了一幅状态转移图,并且对第0行置为全1,这样做的好处是不干扰第一行的放置,因为只有前一行有0,才会影响后一行,这种做法真是太巧妙了。至于后者的做法,令人震惊,他可以解决第一种方法无法解答问题,也就是poj3420.
这个见我的另外一篇博客,传送门:快速幂。
优化的状压dp代码:
#include<cstdio> #include<iostream> #include<cstring> #define ll long long using namespace std; ll dp[1<<11][11]; int n,m; bool check(int x){ //检查第一行合法状态 while(x){ if((x&1)&&(x&2)) x>>=2; //出现11 else if(!(x&1))x>>=1; //出现0 else return false; //出现01 } return true; } void dfs(int r,int c,int s,int scur){ //搜索r-1行状态s可以r行匹配的所有状态scur if(c>m) return; if(c==m) {dp[scur][r]+=dp[s][r-1];return;} if(s>>c&1){ dfs(r,c+1,s,scur); //1->0 if(s>>c&2) dfs(r,c+2,s,scur|3<<c); //11->11 } else dfs(r,c+1,s,scur|1<<c); //0->1 } int main() { while(cin>>n>>m,n){ if(n*m&1) {puts("0");continue;} if(m>n) swap(n,m); memset(dp,0,sizeof dp); for(int s=0;s<1<<m;s++) //初始化第一行 if(check(s)) dp[s][0]=1; for(int i=1;i<n;i++) for(int s=0;s<1<<m;s++) //枚举i-1行状态 dfs(i,0,s,0); cout<<dp[(1<<m)-1][n-1]<<endl; } return 0; }
#include<cstdio> #include<iostream> #include<cstring> #define ll long long #define Maxn 2048 using namespace std; int chg[Maxn*Maxn][2]; ll dp[1<<13][13]; int n,m,tot; void dfs(int c,int s,int scur){ if(c>m) return; if(c==m){ chg[tot][0]=s; chg[tot++][1]=scur; return; } dfs(c+1,s<<1,scur<<1|1); //0->1 dfs(c+1,s<<1|1,scur<<1); //1->0 dfs(c+2,s<<2|3,scur<<2|3); //11->11 } int main() { while(cin>>n>>m,n){ if(m>n) swap(n,m); tot=0; dfs(0,0,0); memset(dp,0,sizeof dp); dp[(1<<m)-1][0]=1; for(int i=1;i<=n;i++){ for(int j=0;j<tot;j++) dp[chg[j][1]][i]+=dp[chg[j][0]][i-1]; } cout<<dp[(1<<m)-1][n]<<endl; } return 0; }