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;
        
    }
}
View Code

因为数组是ull类型,取模其实就是溢出,所以不用管。

posted @ 2020-04-16 21:30  朝暮不思  阅读(210)  评论(0编辑  收藏  举报