[USACO06NOV]Corn Fields G

洛谷AC通道!

一看题目,肯定就是状态压缩的题目。

设$f_{i,k}$表示: 在第$i$行的时候,二进制状态$k$下的方案数量。

首先,我们要保证一个状态下在自己那一行是合法的,那么用$g_i$记录状态$i$是否合法。

继续,我们还要保证上下两行不能有相邻的边,即$(j$ & $k) == 0$, 保证所有的$1,0$全部错开。

如果都合法,那我们直接把状态累计的数量加起来就ok啦!

#include <bits/stdc++.h>
using namespace std;
#define N ((1<<12)+10)
const int mod = (int)1e8;

template <class T>
inline void read(T& a){
    T x = 0, s = 1;
    char c = getchar();
    while(!isdigit(c)){
        if(c == '-') s = -1;
        c = getchar();
    }
    while(isdigit(c)){
        x = x * 10 + (c ^ '0');
        c = getchar();
    }
    a = x * s;
    return ;
}

int F[N];  // 记录每一行的所有状态 
int g[N];  // 记录这种状态是否合法
int dp[13][N], a[N][N]; 
int n, m;

int main(){
    read(m), read(n);
    for(int i = 1; i <= m; i++)
        for(int j = 1; j <= n; j++){
            read(a[i][j]);
            F[i] = (F[i] << 1) + a[i][j]; // 保存每一行的状态 
        }
    for(int i = 0; i < (1 << n); i++)
        g[i] = (!(i & (i << 1))) && (!(i & (i >> 1)));  // g[i] 保证 i 这一状态合法(所有行通用) 
    dp[0][0] = 1;
    for(int i = 1; i <= m; i++){
        for(int j = 0; j < (1 << n); j++){
            if(g[j] && ((j & F[i]) == j)){  // 保证在肥沃的草地上 
                for(int k = 0; k < (1 << n); k++){
                    if((k & j) == 0)   // 保证上一行和这一行没有公共边 
                        dp[i][j] = (dp[i][j] + dp[i-1][k]) % mod; // 累加 
                }
            }
        }
    }
    int ans = 0;
    for(int i = 0; i < (1 << n); i++)
        ans = (ans + dp[m][i]) % mod;  // 最后一行所有的状态的总和 
    cout << ans << endl; 
    return 0;
}
原文地址:https://www.cnblogs.com/wondering-world/p/13493320.html