AcWing337 扑克牌(计数dp)
这道题可以看出与花色无关,甚至与数值无关,我们只需要记住前一个放的值和现在放的不一样就行了
又因为这是一个计数问题,所以我们还要知道前一个值放了之后,有多少种会不符合答案,再算的时候就要去掉他们
因此我们设计状态f[][][][][],表示一张牌,一对牌,三张牌,四张牌同色的个数,然后再枚举一维k表示上一个牌放了后,有多少牌跟他一样的
这样就可以进行记忆化搜索。
考虑放 A 类
f[A−(k==0)][B][C][D][0]f[A−(k==0)][B][C][D][0]
考虑放 B 类
2∗(B−(k==1))∗f[A+1][B−1][C][D][1]2∗(B−(k==1))∗f[A+1][B−1][C][D][1]
考虑放 C 类
3∗(C−(k==2))∗f[A][B+1][C−1][C][D][2]3∗(C−(k==2))∗f[A][B+1][C−1][C][D][2]
考虑放 D 类
4∗(D−(k==3))∗f[A][B][C+1][D−1][C][D][3]
前面的系数是因为,多张同样的牌没有区别,但是各自放的次数要相加,所以要乘系数。
#include<iostream> #include<string> #include<cstring> using namespace std; typedef unsigned long long ull; const int N=14; ull f[14][14][14][14][5]; int cnt[55]; int tot[55]; string s[55]; int sum=1; ull dfs(int a,int b,int c,int d,int k){ auto &x=f[a][b][c][d][k]; if(x!=-1) return x; ull ans=0; if(a) ans+=(a-(k==1))*dfs(a-1,b,c,d,0); if(b) ans+=2*(b-(k==2))*dfs(a+1,b-1,c,d,1); if(c) ans+=3*(c-(k==3))*dfs(a,b+1,c-1,d,2); if(d) ans+=4*d*dfs(a,b,c+1,d-1,3); return x=ans; } int get(char a){ if(a=='T') return 10; if(a=='J') return 11; if(a=='Q') return 12; if(a=='K') return 13; if(a=='A') return 1; return a-'0'; } int main(){ int t; cin>>t; memset(f,-1,sizeof f); for(int i=0;i<=4;i++) f[0][0][0][0][i]=1; while(t--){ int n; cin>>n; int i; memset(cnt,0,sizeof cnt); memset(tot,0,sizeof tot); for(i=1;i<=n;i++){ cin>>s[i]; cnt[get(s[i][0])]++; } for(i=1;i<=13;i++) tot[cnt[i]]++; printf("Case #%d: ",sum++); cout<<dfs(tot[1],tot[2],tot[3],tot[4],0)<<endl; } }
因为数组是ull类型,取模其实就是溢出,所以不用管。
没有人不辛苦,只有人不喊疼