LOJ #10172. 「一本通 5.4 练习 1」涂抹果酱

本文探讨了一个关于涂果酱的组合数学问题,通过使用深度优先搜索(DFS)和动态规划来解决。问题要求在满足特定约束条件下计算不同的涂果酱方案数量。

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

题目描述

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

输入格式

输入共三行。
第一行:N,MN, MN,M;
第二行:KKK;
第三行:MMM 个整数,表示第 KKK 行的方案。
字母的详细含义见题目描述,其他参见样例。

输出格式

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

样例

样例输入

2 2 
1 
2 3

样例输出

3

样例说明

方案一方案二方案三
2 3 
1 2
2 3 
3 1
2 3 
3 2

数据范围与提示

对于 30% 的数据,1≤N×M≤20;
对于 60% 的数据,1≤N≤1000,1≤M≤3;
对于 100% 的数据,1≤N≤10000,1≤M≤5。

 

因为有3种果酱,所以用三进制表示,用dfs生成状态

状态不超过100

#include<cstdio>
#define ll long long
#include<iostream>
const int p=1e6;
using namespace std;
inline int read()
{
	int ret=0;
	char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9')
		ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();
	return ret;
}

int n,m,k,cnt;
const int N=1e4+4,M=6,S=105;
int  a[S],mi[M],c[M],f[N][S];

inline bool check(int x,int y)
{
	int i=0;
	while(x||y)
	{
		i++;
		if(x%3==y%3) return 0;
		x/=3,y/=3;
	}
	if(i<m) return 0;
	return 1; 
}

void dfs(int t,int s)
{
	if(t>m) 
	{
		a[++cnt]=s;
		return;
	}
	if(s/mi[t-2]!=0) dfs(t+1,s);
	if(s/mi[t-2]!=1) dfs(t+1,s+mi[t-1]);
	if(s/mi[t-2]!=2) dfs(t+1,s+mi[t-1]+mi[t-1]);
}

int main()
{
	n=read(),m=read(),k=read();
	
	mi[0]=1;
	for(int i=1;i<=m;i++) mi[i]=(mi[i-1]<<1)+mi[i-1];
	dfs(2,0); dfs(2,1); dfs(2,2);
	
	
	int x=0,loc=0;
	for(int i=1;i<=m;i++) 
		x+=(read()-1)*mi[i-1];
	for(int i=1;i<=cnt;i++)
		if(a[i]==x) 
		{
			loc=i; break;
		}
		
	if(!loc) 
	{
		printf("0\n",0);
		return 0;
	}
	
	f[1][loc]=1;
	for(int i=2;i<=max(k,n-k+1);i++)
		for(int j=1;j<=cnt;j++)
			for(int k=1;k<=cnt;k++)
				if(check(a[j],a[k])) f[i][j]=(f[i][j]+f[i-1][k])%p;
	int ans=0;
	for(int i=1;i<=cnt;i++) ans=(ans+f[k][i])%p;
	int ss=0;
	for(int i=1;i<=cnt;i++) ss=(ss+f[n-k+1][i])%p;
	ans=(ll)ans*ss%p;
	printf("%d\n",ans);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值