题解 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\n",++kase,dp(cnt2[4],cnt2[3],cnt2[2],cnt2[1]));
}
return 0;
}
本作品由happyZYM采用知识共享 署名-非商业性使用-相同方式共享 4.0 (CC BY-NC-SA 4.0) 国际许可协议(镜像(简单版)镜像(完整版))进行许可。
转载请注明出处:https://www.cnblogs.com/happyZYM/p/11638123.html (近乎)全文转载而非引用的请在文首添加出处链接。