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
",ans);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/jihe/p/5238038.html