PKU/POJ 3264 Corn Fields
题目出处:http://poj.org/problem?id=3254
解法: 状态压缩dp
对每一层分开处理,首先从[0,1<<N)枚举状态,如果没有两片草地相邻,而且和草地不冲突就是合法状态。判断是不是有两片相邻可以这样:j&(j<<1) || j&(j>>1),判断草地不合法:假设草地的状态是j,比如sample里面两行的状态分别是111 和10,如果现在枚举状态是i,则如果i|j==j就是合法,没有占用没有草得地。这样可以优化到0ms。
然后从上往下递推,如果是第一行,只要状态是合法这个状态下取法就是1,如果是>1行,对上一行每一个状态枚举,如果两个状态不冲突即没有相邻的草地,那么这个状态的取法就加上上一行那个状态的取法。依次类推。结果就是最后一行的各个状态取法的和。
代码有少量注释。
/* ID: like_091 PROG: spin LANG: C++ */ #include <iostream> #include <cstdio> #include <cstdlib> #include <ctime> #include <map> #include <cstring> #include <string.h> #include <fstream> #include <vector> #include <cmath> #include <algorithm> using namespace std; const int MAX=(1<<12); int M, N, t[15][15], first[15]; int state[15][MAX], index[15], sum[15][MAX], max_value; //获取合法状态 void init(){ int i, j; for (i=1; i<= M; i++){ index[i]=0; for (j=0; j<max_value; j++){ //如果存在相邻的1则不能取 if (j&(j<<1) || j&(j>>1)) continue; //如果和给出的草地不冲突就合法 if ((first[i]|j)==first[i]) state[i][index[i]++]=j; } } } int main(){ int i, j, k; while (scanf("%d%d", &M, &N)!=EOF) { for (i=1; i<= M; i++){ first[i]=0; for (j=1; j<=N; j++){ scanf("%d", &t[i][j]); //草地有无的状态 if (t[i][j])first[i] += 1<<(j-1); } } max_value=1<<N; init(); memset(sum, 0, sizeof(sum)); for (i=1; i<=M; i++){ for (j=0; j < index[i]; j++){ if (i==1){ sum[i][j]=1; continue; } for (k=0; k<index[i-1]; k++){ //如果和上一层冲突则不可以取 if (state[i][j]&state[i-1][k]) continue; sum[i][j]+=sum[i-1][k]; sum[i][j]%=100000000; } } } __int64 tot = 0; for (i=0; i<index[M]; i++){ tot += sum[M][i]; tot %= 100000000; } cout<<tot<<endl; } return 0; }