【ybtoj】【状压dp】种植方案
题意
题目描述
农场主新买了一块长方形的新牧场,这块牧场被划分成m行n列,每一格都是一块正方形的土地。农场主打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。
遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是农场主不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。 如果不考虑草地的总块数,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)
输入格式
第1行:两个整数 m和 n,用空格隔开。
第2到第m+1行:每行包含 n个用空格隔开的整数,描述了每块土地的状态。第i+1行描述了第i行的土地,所有整数均为 0或 1,1表示这块土地足够肥沃, 0则表示这块土地不适合种草。输出格式
一个整数,即牧场分配总方案数除以1e8的余数。
样例
输入样例
2 3 1 1 1 0 1 0
输出样例
9
数据范围与提示
对于100%的数据,满足n,m ∈[1,12]。
分析
十分经典的状压dp,很好想但是涉及一些状压dp的细节和简单优化,类似于模板,需要研究透彻!
优化:
- 用jud数组先预处理出第 i 行 j 的状态可不可行,循环内就可以O(1)判断
- 由于本身每一行有一些位置不能放,即有一些状态不可行,可以给每个合法的状态一个编号,让编号作为第二维,可以减少一些空间和时间(本代码中没有涉及)
注意事项
代码中关键部分和第一次的错误都已经用注释标注出来!ovo
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int INF = 0x3f3f3f3f,mod = 1e8;
int dp[13][1<<13];//dp[i][j]表示第i行状态为j时的方案数
int n,m;
bool jud[13][1<<13];
int a[13][13];
int main()
{
scanf("%d%d",&m,&n);
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
scanf("%d",&a[i][j]);
//预处理
for(int i=1;i<=m;i++)//第i行
for(int j=0;j<(1<<n);j++)//状态为j
{
if(j&(j>>1))//如果有相邻土地被用
{
jud[i][j]=1;
continue;
}
for(int k=1;k<=n;k++)
if( (j&(1<<(k-1)))&& !a[i][n-k+1])//如果有不能种的地方种了东西
{
jud[i][j]=1;
break;
}
}
/*
for(int i=0;i<(1<<n);i++)
dp[1][i]=1;//初始化第1行任何状态都为1
//注意:原来初始化第0行状态都为1,同时i从1开始枚举,这样是不对的
*/
//
dp[0][0]=1;//初始化第0行为0状态,可以转移到第1行任何状态且不重复,和上面的初始化等效
for(int i=1;i<=m;i++)//根据初始化注意i从几开始枚举
for(int j=0;j<(1<<n);j++)//枚举当前行状态
{
if(jud[i][j]) continue;
for(int k=0;k<(1<<n);k++)//枚举上一行状态
{
if(jud[i-1][k]||(k&j)) continue;
dp[i][j]=((ll)dp[i][j]+dp[i-1][k])%mod;
}
}
ll ans=0;
for(int i=0;i<(1<<n);i++)
{
if(jud[m][i]) continue;//注意这里的判断,如果m==1时不加会错
ans=(ans+dp[m][i])%mod;
}
printf("%lld",ans);
return 0;
}