一本通5.4练习1:涂抹果酱

本文介绍了一道编程题目,Sam在为Tyvj两周年庆典制作的蛋糕上涂果酱,但必须遵循相邻区域不能使用同种果酱的规则。题目要求计算在已涂好的第K行基础上,满足条件的涂果酱方案总数,模10^6。该问题通过状态压缩动态规划来解决,将三进制形式用于状态记录。

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

题目描述

Tyvj 两周年庆典要到了,Sam 想为 Tyvj 做一个大蛋糕。蛋糕俯视图是一个 N×M 的矩形,它被划分成 N×M个边长为 1×1 的小正方形区域(可以把蛋糕当成 N行M列的矩阵)。蛋糕很快做好了,但光秃秃的蛋糕肯定不好看!所以,Sam 要在蛋糕的上表面涂抹果酱。果酱有三种,分别是红果酱、绿果酱、蓝果酱,三种果酱的编号分别为 1,2,3。为了保证蛋糕的视觉效果,Admin 下达了死命令:相邻的区域严禁使用同种果酱。但 Sam 在接到这条命令之前,已经涂好了蛋糕第 K 行的果酱,且无法修改。

现在 Sam 想知道:能令 Admin 满意的涂果酱方案有多少种。请输出方案数 mod 10^6。若不存在满足条件的方案,请输出 0。

输入格式

输入共三行。

第一行:N,M;

第二行:K;

第三行:M 个整数,表示第 K 行的方案。

字母的详细含义见题目描述,其他参见样例。

输出格式

输出仅一行,为可行的方案总数。

样例输入

2 2
1
2 3

样例输出

3

样例解释

方案一方案二方案三
2 32 32 3
1 23 13 2

数据范围与提示

对于 30% 的数据,1≤N×M≤20;

对于 60% 的数据,1≤N≤1000,1≤M≤3;

对于 100% 的数据,1≤N≤10000,1≤M≤5。

题解

本题的数据透露了本题的做法——状态压缩dp。但一般的状压dp都是通过二进制数来记录每个单元的0-1的状态(即能不能,选不选等),但本题涉及到三个3种状态,显然无法通过传统的二进制位运算进行状态压缩。故而,我们想到将其转化为一个类似于三进制的数的形式来压缩状态。

代码实现

#include<bits/stdc++.h>
using namespace std;
#define int long long
int state[300],dp[10003][300],s1,g;
int n,m,k,cnt=0,s2;
int const mod=1e6;
inline bool check(int a,int b){//判断两个状态能否作为相邻的两行
	if(a==b)return 0;
	while(a){
		if(a%10==b%10)return 0;
		a/=10;b/=10;
	}
	return 1;
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++)g=g*10+3;//预处理最大情况
	cin>>k;
	for(int i=1;i<=m;++i){
		int x;
		cin>>x;
		if(s1%10==x)s2=1;//判断的k行的状态是否合法
		s1=s1*10+x;
	}
	if(s2){
		cout<<0;
		return 0;
	}
	for(int i=pow(10,m-1);i<=g;i++){//寻找所有可能合法的状态,即相邻两数都不相等的状态
		int flag=1,x=i,p;
		while(x){
			p=x%10;x=x/10;
			if(p==x%10)flag=0;
			if(p==0||p>3)flag=0;
		}
		if(flag)state[++cnt]=i;
		if(i==s1)s2=cnt;//找到第k行状态所对应的编号
	}
	if(k==1)dp[1][s2]=1;
	else{
		for(int i=1;i<=cnt;i++)
			dp[1][i]=1;
	}
	for(int i=2;i<=n;i++){
		if(i!=k){
			for(int k1=1;k1<=cnt;k1++){//枚举前一行的状态
				for(int k2=1;k2<=cnt;k2++){//枚举当前行的状态
					if(check(state[k1],state[k2]))
						dp[i][k2]=(dp[i][k2]+dp[i-1][k1])%mod;
				}
			}
		}
		else{
			for(int k=1;k<=cnt;k++){
				if(check(s1,state[k]))
					dp[i][s2]=(dp[i-1][k]+dp[i][s2])%mod;
			}
		}
	}
	int ans=0;
	for(int i=1;i<=cnt;i++){
		ans=(ans+dp[n][i])%mod;
	}
	cout<<ans;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值