b_vj_Corn Fields(预处理行的状态、合法状态+枚举当前行与上一行的状态)

牧场被划分成M行N列(1≤M≤12; 1≤N≤12),奶牛们喜欢独占一块草地的感觉,于是John不会选择两块相邻的土地;John想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(0表示不可种植,1表示可以)

思路
暴力dp:f[i][j]表示第i行状态为j时的方案数,f[i][j]+=f[i-1][k],row[i]表示网格g的第i行的状态;每次枚举一个状态就检查是否合法;
优化dp:预处理数组valid,valid[j]=0/1表示状态i是否合法

  • 合法就是状态j没有1是相邻的,公式:(j&(j<<1))==0 && (j&(j>>1))==0
#include <cstdio>
#include <iostream>
using namespace std;
const int N=15, mod=1e9, M=1<<N;
int n,m,tot,g[N][N],f[N][M],row[N],valid[M];
void init_st() {
    for (int i=1; i<=n; i++)
    for (int j=1; j<=m; j++) if (g[i][j])
        row[i]|=(1<<j-1);
    for (int j=0; j<tot; j++)
        valid[j]=(j&(j<<1))==0 && (j&(j>>1))==0; //相邻两位没有1
}
int main() {
    std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin>>n>>m;
    for (int i=1; i<=n; i++)
    for (int j=1; j<=m; j++) 
        cin>>g[i][j];
    tot=1<<m, init_st();
    f[0][0]=1;
    for (int i=1; i<=n; i++)
    for (int j=0; j<tot; j++) if (valid[j] && (row[i]&j)==j) { //j合法,且row[i]包含状态j(j是row[i]的子集)
        for (int k=0; k<tot; k++) if (valid[k] && (j&k)==0)
            f[i][j]=(f[i][j]+f[i-1][k])%mod;
    }
    int ans=0;
    for (int j=0; j<tot; j++) ans=(ans+f[n][j])%mod;
    cout<<ans; 
    return 0;
}
原文地址:https://www.cnblogs.com/wdt1/p/13937439.html