[状压DP] 玉米田Corn Fields

文章目录


题目

题目描述

农场主 J o h n John John新买了一块长方形的新牧场,这块牧场被划分成 M M M N N N ( 1 ≤ M ≤ 12 ; 1 ≤ N ≤ 12 ) (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) (1M12;1N12),每一格都是一块正方形的土地。 J o h n John John打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。

遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是 J o h n John John不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。

J o h n John John想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)

输入

第一行:两个整数 M M M N N N,用空格隔开。

2 2 2到第 M + 1 M+1 M+1行:每行包含 N N N个用空格隔开的整数,描述了每块土地的状态。第 i + 1 i+1 i+1行描述了第 i i i行的土地,所有整数均为 0 0 0 1 1 1,是 1 1 1的话,表示这块土地足够肥沃, 0 0 0则表示这块土地不适合种草。

输出

一个整数,即牧场分配总方案数除以 100000000 100000000 100000000的余数。

样例输入

2 3
1 1 1
0 1 0

样例输出

9

题解

简单的状压,我们定义二维的 d p [ i ] [ j ] dp[i][j] dp[i][j]表示到第 i i i行符合条件的第 j j j个状态的种草总方案
转移很好想 d p [ i ] [ j ] = ∑ d p [ i − 1 ] [ k ] dp[i][j] = \sum dp[i - 1][k] dp[i][j]=dp[i1][k]

我们只需要先预处理出每一行符合条件的状态( 1 1 1表示种草, 0 0 0表示不种),然后三重循环转移,最后枚举一下求和就可以了

具体实现见代码

代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

#define LL long long

const LL mod = 100000000;

LL ans;
LL dp[15][40000];
int m, n;
int sum[15], a[15][15], zt[15][40000];

bool check(int x, int y) {
	int w = n;
	while(y) {
		if(a[x][w] == 0 && y % 2 == 1)
			return 0;
		y >>= 1;
		w --;
	}
	return 1;
}

void prepare() {
	int p = (1 << n) - 1;
	for(int i = 0; i <= p; i ++)
		if(!((i << 1) & i)) {
			for(int j = 1; j <= m; j ++) {
				if(check(j, i))
					zt[j][++ sum[j]] = i;
			}
		}
}

int main() {
	scanf("%d%d", &m, &n);
	for(int i = 1; i <= m; i ++)
		for(int j = 1; j <= n; j ++) {
			int x;
			scanf("%d", &x);
			if(x == 1) a[i][j] = 1;
			else a[i][j] = 0;	
		}
	prepare();
	for(int i = 1; i <= sum[1]; i ++)
		dp[1][i] = 1;
	for(int i = 2; i <= m; i ++)
		for(int j = 1; j <= sum[i]; j ++)
			for(int l = 1; l <= sum[i - 1]; l ++)
				if(!(zt[i][j] & zt[i - 1][l]))
					dp[i][j] = (dp[i][j] + dp[i - 1][l]) % mod;
	for(int j = 1; j <= sum[m]; j ++)
		ans = (ans + dp[m][j]) % mod;
	printf("%lld\n", ans);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值