1 solutions

  • 0
    @ 2025-3-28 13:21:00

    直接搜索会导致超时,因为可以选择的数量太多了,那么我们可以将所有的牌分成两类(顺子和非顺子),梳子需要考虑牌的顺序,而非顺子不需要考虑牌的顺序,只需要考虑对应的牌的数量就可以了。

    #include<bits/stdc++.h>
    using namespace std;
    const int INF=100;
    int f[24][12][8][6];
    int dp(int s[])
    {
        int sum=0;
        for(int i=1;i<=4;i++) //枚举每类牌
        {
            if(s[i]<0) //不合法的序列
            {
                return INF;
            }
            else
            {
                sum+=s[i];
            }
        }
        int& v=f[s[1]][s[2]][s[3]][s[4]];
        if(v!=-1) return v; //已经找到过
        if(!sum) return v=0; //已经出完
        v=INF;
        for(int k=1;k<=4;k++) //枚举出牌
        {
            for(int i=k;i<=4;i++) //从哪里出
            {
                s[i]--,s[i-k]++; //出了以后的变化
                v=min(v,dp(s)+1); //更新答案
                if(k==3) //3带
                {
                    for(int x=1;x<=2;x++)
                    {
                        for(int y=x;y<=4;y++)
                        {
                            s[y]--,s[y-x]++;
                            v=min(v,dp(s)+1);
                            s[y]++,s[y-x]--;
                        }
                    }
                }
                else if(k==4) //4带
                {
                    for(int x=1;x<=2;x++)
                    {
                        for(int y=x;y<=4;y++)
                        {
                            for(int z=x;z<=4;z++)
                            {
                                s[y]--,s[y-x]++;
                                s[z]--,s[z-x]++;
                                v=min(v,dp(s)+1);
                                s[y]++,s[y-x]--;
                                s[z]++,s[z-x]--;
                            }
                        }
                    }
                   
                } 
                s[i]++,s[i-k]--;
            }
        }
        return v;
    }
    bool check(int c[],int l,int r,int k) //判断是否可以出
    {
        for(int i=l;i<=r;i++)
        {
            if(c[i]<k)
            {
                return false;
            }
        }
        return true;
    }
    void add(int c[],int l,int r,int k) //范围变化
    {
        for(int i=l;i<=r;i++)
        {
            c[i]+=k;
        }
    }
    int dfs(int c[]) //搜索
    {
        for(int i=0;i<14;i++) //非法状态
        {
            if(c[i]<0)
            {
                return INF;
            }
        }
        int s[5]={0};
        for(int i=0;i<14;i++) //统计数量
        {
            s[c[i]]++;
        }
        int res=dp(s); //不用顺子
        int len[]={0,5,3,2};
        for(int k=1;k<=3;k++) //顺子类别
        {
            for(int i=2;i<=13;i++) //起点
            {
                for(int j=i+1;j<=13;j++)  //终点
                {
                    if(j-i+1>=len[k]&&check(c,i,j,k))
                    {
                        add(c,i,j,-k);
                        res=min(res,dfs(c)+1);
                        add(c,i,j,k);
                    }
                }
            }
        }
        return res;
    }
    int main()
    {
        memset(f,-1,sizeof f);
        int T,n;
        cin>>T>>n;
        while(T--)
        {
            int cnt[14]={0};
            for(int i=0;i<n;i++)
            {
                int a,b;
                cin>>a>>b;
                if(a==1) a=13; //a变成k的后面
                else if(a) a--; //其它前移一次
                cnt[a]++; //统计对应牌的数量
            }
            cout<<dfs(cnt)<<endl;
        }
        return 0;
    }
    
    • 1

    Information

    ID
    1114
    Time
    1000ms
    Memory
    256MiB
    Difficulty
    10
    Tags
    # Submissions
    1
    Accepted
    1
    Uploaded By