[BZOJ1079/Luogu2476][SCOI2008]着色方案

题目链接:

BZOJ1079

Luogu2476

记忆化搜索+(DP)

首先看到数据范围就知道状态是个(15^5)的级别而不是(5^{15})了。

那么显然设(f_{a,b,c,d,e})表示还有(a)种颜色剩(1)个,(b)种剩两个,(cdots)(e)种剩(5)个时的方案数。

但是如何判断两个颜色不能相邻呢?

如果记录上一次的颜色,那么这次判断也需要颜色,则状态爆炸。

发现无论上次是什么颜色,只需要判断是否相同即可。

那么就记录上一次选的颜色现在还剩几个,在转移时如果相同那么方案数就要少一种选择。

综上,我们得到了如下的转移式:

[f_{a,b,c,d,e,PreRem}+=f_{a-1,b,c,d,e,0}*(a-[PreRem==1]) (a>0) ]

其中(PreRem)代表上一次选的颜色现在剩几个。

如果还有(a)个颜色(1),那么可以更新答案,(PreRem)变为(0),此时有(a)种颜色可选,故答案(*a)

同时如果(PreRem==1),那么说明这(a)种颜色中有一种与之前重复,不能选,只剩(a-1)种颜色可选,答案(*(a-1))

对于其他(4)项转移方程也同理(别忘记在低项加上刚用掉的颜色)。

时间复杂度 (O(k^c))

#include <cstdio>
#include <cstring>

int k,c[10];
int f[16][16][16][16][16][6];
const int Mod=1000000007;

int DP(int a,int b,int c,int d,int e,int PreRem)
{
	if(!a&&!b&&!c&&!d&&!e)return 1;
	int &Ans=f[a][b][c][d][e][PreRem];
	if(~Ans)return Ans;
	Ans=0;
	if(a)(Ans+=DP(a-1,b,c,d,e,0)*1LL*(PreRem==1?a-1:a)%Mod)%=Mod;
	if(b)(Ans+=DP(a+1,b-1,c,d,e,1)*1LL*(PreRem==2?b-1:b)%Mod)%=Mod;
	if(c)(Ans+=DP(a,b+1,c-1,d,e,2)*1LL*(PreRem==3?c-1:c)%Mod)%=Mod;
	if(d)(Ans+=DP(a,b,c+1,d-1,e,3)*1LL*(PreRem==4?d-1:d)%Mod)%=Mod;
	if(e)(Ans+=DP(a,b,c,d+1,e-1,4)*1LL*(PreRem==5?e-1:e)%Mod)%=Mod;
	return Ans;
}

int main()
{
	scanf("%d",&k),memset(f,-1,sizeof f);
	for(int cs;k--;)scanf("%d",&cs),++c[cs];
	printf("%d
",DP(c[1],c[2],c[3],c[4],c[5],0));
	return 0;
}
原文地址:https://www.cnblogs.com/LanrTabe/p/10178395.html