Corn Fields

题目描述
农场主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的余数。

输入输出样例
输入 #1
2 3
1 1 1
0 1 0
输出 #1
9

题目链接
思路
算是状压dp的入门题,蒟蒻的我看懂了所以来整理一下思路

  1. 将草地的原始状态压缩为一个整数 数组F[i]表示第i行草地状态对应的整数
    「例如样例中的1 1 1,用数字7表示(7的二进制表示为111)」
  2. (暂时认为草地都是肥沃的)总共有m列,那么所有可能的二进制表示有2^m(1<<m)种,也就是说我们有 2^m(1<<m)次放牧选择方案,其中不乏有相邻的方案,我们需要把相邻的方案去掉,g[i]为真,表示i这个数对应的放牧方案在一行中没有相邻
    「判断是否相邻的方法:如果(i&(i<<1)==0)&&(i&(i>>1)==0)则方案成立,这里用到了位运算的性质,往左和往右移1的结果与原数做&运算,如果是0,就说明原数是01交替排列的,不清楚的可以手动模拟」
  3. 处理放牧方案,使它只在肥沃草地上放牧。对于第i行的草地状态F[i]和某一放牧状态j,如果枚举到的F[i]&j==j即说明了两个状态完全相同,没有放到贫瘠草地的情况。
  4. 对于第i行不和i-1行有连通草地,枚举上一行方案k,该行方案j&k==0即可满足条件。
#include<iostream>
#include<string>
#include<string.h>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
//#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int F[15];
int a[15][15];
bool g[1 << 12 + 5];
int dp[15][1 << 12 + 5];
int mod = 1e8;
int main()
{ 
	int n, m;
	cin >> n >> m; //n行m列
	for(int i=1;i<=n;i++)
	{ 
		for (int j = 1; j <= m; j++)
		{
			cin >> a[i][j]; 
			F[i] = (F[i] << 1) + a[i][j]; //每次读入一个数后,往F[i]尾部追加,当列遍历完后,F[i]就表示第i行的草地状态
		}
	}
	for (int i = 0; i < (1 << m); i++)  //判断每一行所有可能的方案种数,去掉相邻的
	{
		g[i] = (!(i&(i << 1))) && (!(i & (i >> 1))); 
	}
	dp[0][0] = 1; //初始化
	for (int i = 1; i <= n; i++) //遍历所有行
	{
		for (int j = 0; j < (1 << m); j++) //遍历行的方案
		{
			if (g[j] && ((j&F[i]) == j)) //如果这种方案没有相邻,并且是在肥沃土地上种草(这里j与F[i]不完全相同也可能是在肥沃土地上种草,但这里其实是考虑到i的上一行,j必须与F[i]相同才能考虑上一行放牧类型所有的情况)
			{
				for (int k = 0; k < (1 << m); k++) //遍历上一行的方案
				{
					if ((k&j) == 0) //如果不相邻
					{
						dp[i][j] = (dp[i][j] + dp[i - 1][k]) % mod; //增加上一行为方案k的方案数
					}
				}
			}
		}
	}
	int ans = 0;
	for (int i = 0; i < (1 << m); i++)
	{
		ans = (ans + dp[n][i]) % mod;//dp[n][i]表示第n行是方案i的时候的方案数,所以要统计所有方案数时,需将所有可行的方案总数累加
	}
	cout << ans << endl;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值