NOI模拟(10.29)T2 棋盘

棋盘

题目背景:

10.29 NOI模拟T2

分析:状压DP

 

第一眼就知道是状压DP,第一反应就知道大致方法,然而,我写不出来啊woc······绝望·······

讲题,定义ans[size]表示魔法值大于等于size的方案数共有多少种,那么显然魔法值为size的方案数为ans[size] - ans[size + 1],现在我们来考虑怎么获得ans[size],我们先枚举当前的size,然后开始进行dp,定义dp[i][j]表示,完成前i行的染色,最后一行的状态为j的方案数,这里的j是一个size进制数,j的第k位表示k ~ k + size - 1当中,向上全部都被染过色的最小的层数,即,以k ~ k + size - 1为底,最大的被完全染色的矩形的高是多少,然后我们可以枚举新的一行的状态,先判断,i ~ i + size - 1的染色程度,然后枚举上一行的j进行转移,如果i ~ i + size - 1均被染色,那么转移位置的j的第i位应该比上一行的加1,否则直接清0,如果发现之前j的第i位已经为size - 1了,并且这一次也全部被染色,则表示不能转移,否则会形成魔法值为size的方案。然后ans[size]就等于总的染色方案数减去所有dp[n][i]之和,(dp[n][i]表示第n行状态为i的满足魔法值小于size的方案数,枚举i即可),至此就搞定啦,注意边界和清空即可。复杂度····我也不想算。

Source:

/*
	created by scarlyw
*/
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cctype>
#include <vector>
#include <set>
#include <queue>
#include <ctime>

const int MAXN = 10;
const int MAXX = 1000000;
const int mod = 1000000000 + 7;

int m, n;
int ans[MAXN], ban[MAXN], p[MAXN];
int dp[MAXN][MAXX];
bool able[MAXN];
char map[MAXN][MAXN];

inline void add(int &x, int t) {
	x += t, (x >= mod) ? (x -= mod) : 0;
}

int main() {
	scanf("%d", &n), m = 1;
	for (int i = 1; i <= n; ++i) scanf("%s", map[i]);
	for (int i = 1; i <= n; ++i)
		for (int j = 0; j < n; ++j)
			if (map[i][j] == 'o') add(m, m);
			else ban[i] |= (1 << j);
	ans[0] = 1, ans[1] = m - 1, (ans[1] < 0) ? (ans[1] += mod) : 0;
	for (int size = 2; size <= n; ++size) {
		int end = n - size + 1;
		p[0] = 1, dp[0][0] = 1;
		for (int i = 1; i <= end; ++i) p[i] = p[i - 1] * size;
		for (int i = 1; i < p[end]; ++i) dp[0][i] = 0;
		for (int i = 1; i <= n; ++i) {
			for (int j = 0; j < p[end]; ++j) dp[i][j] = 0;
			for (int cur = 0, s = (1 << n); cur < s; ++cur) {
				if (cur & ban[i]) continue ;
				for (int j = 0; j < end; ++j) able[j] = true;
				for (int j = 0; j < n; ++j) {
					if (cur & (1 << j)) continue ;
					for (int l = 0; l < end; ++l) {
						if (j >= l && j < l + size)
							able[l] = false;
					}
				}
				for (int last = 0; last < p[end]; ++last) {
					if (dp[i - 1][last] == 0) continue ;
					int pos = last, suc = 0;
					for (int j = 0; j < end; ++j) {
						int t = pos / p[j] % size;
						if (!able[j]) t = 0;
						else if (t != size - 1) ++t;
						else {
							suc = -1;
							break ;
						}
						suc += t * p[j];
					}
					if (suc == -1) continue ;
					add(dp[i][suc], dp[i - 1][last]);
				}
			}
		}
		ans[size] = 0;
		for (int i = 0; i < p[end]; ++i) add(ans[size], dp[n][i]);
		ans[size] = m - ans[size], (ans[size] < 0) ? (ans[size] += mod) : 0;
		ans[size - 1] = ans[size - 1] - ans[size];
		(ans[size - 1] < 0) ? (ans[size - 1] += mod) : 0;
	}
	for (int i = 0; i <= n; ++i) std::cout << ans[i] << '\n';
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值