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

题目描述

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 3 2 3 2 3
1 2 3 1 3 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
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<

你可能感兴趣的:(c++,算法,动态规划)