poj 3254 Corn Fields
Posted 罚时自动机
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了poj 3254 Corn Fields相关的知识,希望对你有一定的参考价值。
题意:给一个n*m的图,图上点有两种状态,0和1,1表示可以标记,0不可以,如果一个点标记之后,他的上下左右都不可以标记,求所有的可能的情况
分析:n,m<=12,我们可以想到用二进制表示每个点的状态,是否标记,那么dp[i][j]=sum(dp[i-1][k]),i表示行,j表示状态,比如5,二进制位101,表示1,3标记,其他的点不标记
我开了两个vector保存上一次的所有符合要求的枚举量和记录这次的枚举量,这样枚举上一行的时候不需要再枚举所有的可能量,但是vector速度比较慢,效果并不好
16ms过的,如果用数组模拟vector,会快很多,动手强的自己搞一下
#include<cstdio> #include<cstring> #include<vector> #include<iostream> using namespace std; const int maxn=13; const int mod=1e8; int sn,map[maxn][maxn],state[1<<maxn],dp[2][1<<maxn]; int n,m; void cal(){ sn=0; for(int i=0;i<(1<<m);i++) if(!(i&(i<<1))) state[sn++]=i; } //判断状态j在第i行是否可行 bool judge(int i,int j){ for(int k=0;k<m;k++) if(!map[i][k]&&(state[j]&(1<<k))) return false; return true; } int main(){ scanf("%d%d",&n,&m); cal(); memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++) for(int j=0;j<m;j++) scanf("%d",&map[i][j]); vector<int> d[2];//保存上一行的正确状态,减少枚举量,也可以用滑动数组实现 d[0].push_back(0); dp[0][0]=1; for(int i=1;i<=n;i++){ int p=(i-1)%2; d[i%2].clear(); for(int j=0;j<sn;j++){ dp[i%2][state[j]]=0; if(!judge(i,j))continue; for(int k=0;k<d[p].size();k++) if(state[j]&d[p][k]) continue; else{ dp[i%2][state[j]]+=dp[p][d[p][k]]; dp[i%2][state[j]]%=mod; } if(dp[i%2][state[j]]) d[i%2].push_back(state[j]); } } int ans=0,p=n%2; for(int i=0;i<d[p].size();i++){ ans+=dp[p][d[p][i]]; ans%=mod; } printf("%d\n",ans); return 0; }
以上是关于poj 3254 Corn Fields的主要内容,如果未能解决你的问题,请参考以下文章