牧场被划分成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;
}