[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\n",DP(c[1],c[2],c[3],c[4],c[5],0));
	return 0;
}
posted @ 2018-12-26 14:21  LanrTabe  阅读(166)  评论(0编辑  收藏  举报