【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的细节和简单优化,类似于模板,需要研究透彻!

优化:

  1. 用jud数组先预处理出第 i 行 j 的状态可不可行,循环内就可以O(1)判断
  2. 由于本身每一行有一些位置不能放,即有一些状态不可行,可以给每个合法的状态一个编号,让编号作为第二维,可以减少一些空间和时间(本代码中没有涉及)

注意事项

代码中关键部分和第一次的错误都已经用注释标注出来!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;
}
posted @ 2021-07-22 18:54  conprour  阅读(74)  评论(0编辑  收藏  举报