JZOJ 7047. 2021.04.07【2021省赛模拟】染色(计数+DP)

这篇博客探讨了一道关于棋盘染色问题的竞赛题目,其中涉及动态规划策略来统计不同染色方案。作者首先介绍了问题背景,然后详细解释了如何通过规定合法染色方案来避免重复统计,并提出了一个O(n^3)时间复杂度的解决方案。最后,给出了C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JZOJ 7047. 2021.04.07【2021省赛模拟】染色

题目大意

  • 一个n∗mn*mnm的棋盘,一次染色可以把任何一行或任何一条左下-右上方向的对角线上所有格子都染黑,求各种染色方案可以得到的不同最终形态数。
  • n,m≤500n,m\le500n,m500

题解

  • 最终形态比较难统计,可以改为统计不同的染色方案。由于不同的染色方案可能会得到相同的最终形态,所以需要规定一些染色方案为“合法”的以保证不重复统计。
  • 可以让每种最终形态对应上唯一一种染色方案。具体的,使最终形态中有整行的必须由整行的染,其他剩余未染的才用对角线染。
  • 把行从上到下1−n1-n1n标号,把对角线从左上到右下1−(n+m−1)1-(n+m-1)1(n+m1)标号,则可以知道怎么样的染色方案为“合法“的:
  • 1、第iii行不染色,则iiii+m−1i+m-1i+m1号对角线不能都染色,不然会覆盖满一整行;
  • 2、第iii条对角线染色,则max(i+1−m,1)max(i+1-m,1)max(i+1m,1)min(n,i)min(n,i)min(n,i)号横行(即这条对角线经过的横行)不能都染色,不然这条对角线染色没有用。
  • 更抽象地,看作长为nnnn+m−1n+m-1n+m1的两个序列A,BA,BA,B,每个数可以选或不选,AiA_iAi不选则BiB_iBi往后不能连续选超过mmm个,BiB_iBi选则Amin(i,n)A_{min(i,n)}Amin(i,n)往前不能连续选超过min(n,i)−ma(i+1−m,1)+1min(n,i)-ma(i+1-m,1)+1min(n,i)ma(i+1m,1)+1个。
  • 发现任一序列iii处的限制总是和另一序列的iii处相关,则两序列同一位置一起转移,BBB序列多出的部分再单独转移即可。
  • fi,j,kf_{i,j,k}fi,j,k表示转移到第iii位,BBB序列从当前还能往后连续选jjj个,AAA序列到当前已经连续选了kkk个,分AiA_iAi选或不选,BiB_iBi选或不选来转移。要注意在i>ni>ni>n时,kkk表示的是AAAnnn已经连续选的个数,因为BBB限制的是Amin(i,n)A_{min(i,n)}Amin(i,n)
  • 第一维可以滚动,时间复杂度O(n3)O(n^3)O(n3)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 510
int f[2][N][N];
int main() {
	int n, m, P, i, j, k;
	scanf("%d%d%d", &n, &m, &P);
	f[0][m + 1][0] = 1;
	for(i = 0; i <= n + m - 2; i++) {
		int o = i % 2;
		memset(f[o ^ 1], 0, sizeof(f[o ^ 1]));
		for(j = 0; j <= m + 1; j++) {
			for(k = 0; k <= n; k++) if(f[o][j][k]) {
				if(i + 1 <= n) {
					(f[!o][m + 1][0] += f[o][j][k]) %= P; // a no b no
					(f[!o][m + 1][k + 1] += f[o][j][k]) %= P;// a yes b no
					if(j && m > 1) (f[!o][min(m - 2, j > m ? j : j - 1)][0] += f[o][j][k]) %= P;//a no b yes
					if(j && k < i - max(i + 1 + 1 - m, 1) + 1) (f[!o][j > m ? j : j - 1][k + 1] += f[o][j][k]) %= P; //a yes b yes
				}
				else {
					(f[!o][m + 1][k] += f[o][j][k]) %= P; // a no b no
					if(j && k < n - max(i + 1 + 1 - m, 1) + 1) (f[!o][j > m ? j : j - 1][k] += f[o][j][k]) %= P;//a no b yes
				}
			}	
		}
	}
	int ans = 0;
	for(j = 0; j <= m + 1; j++)
		for(k = 0; k <= n; k++) (ans += f[(n + m - 1) % 2][j][k]) %= P;
	printf("%d\n", ans);
	return 0;
}

自我小结

  • 这题想到统计染色方案后,先写了暴力DFS,在剪枝优化的过程中发现了限制的关联性,于是顺理成章地用DP设状态解决。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值