题解 CH5E26 【扑克牌】

题目链接:Link

Problem

Solution

首先不难想到,至少要记录四个状态:“有4种颜色的牌的面值个数”“有3种颜色的牌的面值个数”……
然后尝试再记录一个状态:当前状态动的那个牌的面值的颜色数。但这样会导致边界条件特判过于麻烦。
考虑进行如下容斥:
当动有1种颜色的牌时,显然 $ add = c1 * f(c4,c3,c2,c1-1) $ 。
当动有2种颜色的牌时,显然 $ add += 2 * c2 * dp(c4,c3,c2-1,c1+1) $ ,但我们要考虑减去连出2次相同面值的牌的行为,即 $ add -= 2 * c2 * dp(c4,c3,c2-1,c1) $
当动有3种颜色的牌时,显然要加上至少连出1次同样面值的牌的行为,但由于后面2~3位已保证不会重复,多减去了,所以还要加回来。通4种颜色的情况一样,容斥即可。

Code

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
typedef unsigned long long ULL;
int T,kase,n,h1[128],cnt1[14],cnt2[5];
ULL f[14][14][14][14];
ULL dp(int c4,int c3,int c2,int c1)
{
	ULL &res=f[c4][c3][c2][c1];
	if(res!=-1ull) return res;
	res=0;
	if(c1>0) res+=1ull*c1*dp(c4,c3,c2,c1-1);
	if(c2>0) res+=2ull*c2*(dp(c4,c3,c2-1,c1+1)-dp(c4,c3,c2-1,c1));
	if(c3>0) res+=3ull*c3*(dp(c4,c3-1,c2+1,c1)-2ull*(dp(c4,c3-1,c2,c1+1)-dp(c4,c3-1,c2,c1)));
	if(c4>0) res+=4ull*c4*(dp(c4-1,c3+1,c2,c1)-3ull*(dp(c4-1,c3,c2+1,c1)-2ull*(dp(c4-1,c3,c2,c1+1)-dp(c4-1,c3,c2,c1))));
	return res;
}
int main()
{
	#ifdef local
	freopen("pro.in","r",stdin);
	#endif
	scanf("%d",&T);
	h1['A']=1;
	for(int i='2';i<='9';i++) h1[i]=i-'0';
	h1['T']=10; h1['J']=11; h1['Q']=12; h1['K']=13;
	memset(f,-1,sizeof(f));
	f[0][0][0][0]=1;
	while(T-->0)
	{
		scanf("%d",&n);
		char s[3];
		FU(i,1,13) cnt1[i]=0;
		for(int i=0;i<n;i++)
		{
			scanf("%s",s);
			cnt1[h1[s[0]]]++;
		}
		FU(i,1,4) cnt2[i]=0;
		FU(i,1,13) cnt2[cnt1[i]]++;
		printf("Case #%d: %llu
",++kase,dp(cnt2[4],cnt2[3],cnt2[2],cnt2[1]));
	}
	return 0;
}
原文地址:https://www.cnblogs.com/happyZYM/p/11638123.html