P1879 [USACO06NOV] Corn Fields G(状压dp)
题目链接:https://www.luogu.com.cn/problem/P1879
首先先看看这个:https://www.cnblogs.com/lipu123/p/18404495
农场主John 新买了一块长方形的新牧场,这块牧场被划分成 \(M\) 行 \(N\) 列\((1≤M≤12,1≤N≤12)\),每一格都是一块正方形的土地。 John 打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。
遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是 John 不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。
John 想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)
输入格式
第一行:两个整数 \(M\) 和 \(N\),用空格隔开。
第 \(2\) 到第 \(M+1\) 行:每行包含 \(N\) 个用空格隔开的整数,描述了每块土地的状态。第 \(i+1\) 行描述了第 \(i\) 行的土地,所有整数均为 \(0\) 或 \(1\),是 \(1\) 的话,表示这块土地足够肥沃,\(0\) 则表示这块土地不适合种草。
输出格式
一个整数,即牧场分配总方案数除以100,000,000 的余数。
样例
输入
2 3
1 1 1
0 1 0
输出
9
这个和这个题很像 https://www.cnblogs.com/lipu123/p/18404495
设 f[i,j]表示第 \(i\) 行的方案是 \(j\)(是一个二进制数,用十进制来存储,第 \(k\) 位是
1/0(二进制)表示选或不选)时的方案数。
下面的 \(j,k\) 都是一个合法的方案。
若已经进行到 \(i\) 行,此时的方案是 \(j\),上一行的方案是 \(k\)。
有一个特殊条件(边界):i=1。
既然是第一行,那么它的所以合法方案都是正确的,所以边界是:f[1,j]=1
也可以很容易地想到本行的合法方案的方案数是上一行的所有合法方案数,也就是:
\(f[i,j]=f[i,j]+f[i−1,k]\)(这里的j和k都是合法的)
这个代码是首先预处理出来全部的合法的state(就是没有挨着的1),储存起来,然后再遍历每一行,然后对于该行的合法\(state_j\),枚举他的上一行的合法的\(state_k\)。然后加起来。
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=13;
ll f[maxn][1<<maxn];//前i行,并且第i行状态为j的方案数
ll a[maxn][maxn];
ll sit[11110];
ll n,m,cnt=0;
const int mod=100000000;
void init(){
for(int i=0;i<(1<<m);i++){
if(!(i&(i<<1))&&!(i&(i>>1))){//因为要互不侵犯,所以,两个国王之间必须隔一个,这是判断是否满足题意国王之间不相互攻击;
sit[++cnt]=i;//找到了满足的,记录这个状态;
}
}
}
int judge(int x,int i){
for(int j=0;j<m;j++){
if(((x>>j)&1)&&a[i][j]==0){
return 0;
}
}
return 1;
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>a[i][j];
}
}
init();
//处理第0行
for(int i=1;i<=cnt;i++){
if(judge(sit[i],0)){
f[0][i]=1;
}
}
for(int i=1;i<n;i++){
for(int j=1;j<=cnt;j++){
if(judge(sit[j],i-1)){
for(int k=1;k<=cnt;k++){
if(judge(sit[k],i)&&!(sit[k]&sit[j])){
f[i][k]=(f[i][k]+f[i-1][j])%mod;
}
}
}
}
}
ll ans=0;
for(int i=1;i<=cnt;i++){
ans=(ans+f[n-1][i])%mod;
}
cout<<ans<<endl;
}
/*
5 6
1 1 1 0 1 1
0 0 1 1 0 1
1 1 0 1 1 0
1 1 1 0 1 1
1 1 1 0 1 1
*/
这个代码是没有预处理的:
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod=100000000;
ll n,m,ans;
ll f[13][1<<13];
ll g[1<<12],h[1<<12],a[13][13];
int judge(int x,int i){
for(int j=0;j<m;j++){
if(((x>>j)&1)&&a[i][j]==0){
return 0;
}
}
return 1;
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>a[i][j];
}
}
for(int i=0;i<(1<<m);i++){
if(!(i&(i<<1))&&!(i&(i>>1))){//判断是否该列合法,即没有连续的1
g[i]=1;
}
}
for(int i=0;i<(1<<m);i++){
if(g[i]&&judge(i,0)){
f[0][i]=1;
}
}
for(int i=1;i<n;i++){
for(int j=0;j<(1<<m);j++){
if(g[j]&&(judge(j,i-1))){
for(int k=0;k<(1<<m);k++){
if(!(k&j)&&g[k]&&judge(k,i)){
f[i][k]=(f[i][k]+f[i-1][j])%mod;//记录
}
}
}
}
}
for(int i=0;i<(1<<m);i++){
ans=(ans+f[n-1][i])%mod;
}
cout<<ans<<endl;
}