loj 6434「PKUSC2018」主鬥地

loj

最可做的鬥地主系列題(?)

顯然的想法是爆搜可憐的牌,然後接着找是否有合法出牌方案.因爲總的方案數只有幾百萬種,所以可以直接枚舉每種方案

然後是優化check過程.首先可以發現對子三張牌順子連對三順可以拆成若干單牌,飛機可以拆成若干三帶一或三帶二,所以只有我們只用考慮單牌,三帶一,三帶二,四帶二.如果只考慮單牌,那麼一定是兩者的牌分別排好序後,可憐某張牌要嚴格小於網友的對應位置的牌才合法,所以這個可以從大到小枚舉牌大小,然後看可憐的每種牌是否都有網友的更大的牌可以配上對,複雜度爲\(O(14)\),也就是牌的種類數

然後考慮剩下的三種\(x\)\(y\).我們把所有\(x\)\(y\)看成先選好\(x\),然後選\(y\).所以就可以讓兩個人先只打三張或四張一樣的牌,並且記錄下三張牌或四張牌的個數,然後對應帶的一些散牌後面處理.因爲這些散牌沒有限制大小關係,所以實際上\(x\)\(y\)的作用是把一些點數大導致配不上對的牌先消掉.一組三張牌可以帶單牌或對子,一組四張牌可以帶兩張單牌,所以我們可以枚舉打幾次對子,注意到對子不用枚舉各種打法,因爲我們要儘量消掉可憐的大的牌,所以最優的方法是每次選擇可憐最大的對子消掉;同樣貪心的考慮,我們用網友最小的對子與其配對,顯然也是最優的.最後還剩下一些三張牌或四張牌沒有帶上東西(設有\(a\)組三張牌,\(b\)租四張牌),因爲前面枚舉了對子,那麼剩下的三張牌我們強制其帶單牌,所以就是還可以選出至多\(a+2b\)單牌配對,剩下的牌就只能用單牌一一對應去check.具體來講,如果在單牌check過程中有\(c\)張牌沒被配對,那麼如果滿足\(c\le a+2b\),那麼這個就是合法方案,這是因爲兩個人都會有\(c\)張牌沒被配對好,那麼這\(c\)張牌被三張牌或四張牌帶上就行了.複雜度\(O(能過)\)

#include<bits/stdc++.h>
#define LL long long
#define uLL unsigned long long
#define db double

using namespace std;
int rd()
{
    int x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
char cc[20];
int mpp[127];
void inii()
{
    mpp['4']=0;
    mpp['5']=1;
    mpp['6']=2;
    mpp['7']=3;
    mpp['8']=4;
    mpp['9']=5;
    mpp['T']=6;
    mpp['J']=7;
    mpp['Q']=8;
    mpp['K']=9;
    mpp['A']=10;
    mpp['2']=11;
    mpp['w']=12;
    mpp['W']=13;
}
int lm[14]={4,4,4,4,4,4,4,4,4,4,4,4,1,1};
int ans,ntz[14],karen[14];
bool ck(int pk,int ls,int c1,int c2)
{
    int sm=0;
    for(int i=13;~i;--i)
    {
        sm-=karen[i];
        sm=max(sm,0)+ntz[i];
    }
    if(sm<=c1+c2*2) return 1;
    int m1,m2;
    bool ok=0;
    for(;pk>=3;--pk)
    {
        m1=ls;
        while(m1<=13&&karen[m1]<pk) ++m1;
        while(m1<=13)
        {
            m2=m1+1;
            while(m2<=13&&ntz[m2]<pk) ++m2;
            if(m2>13) break;
            while(m2<=13)
            {
                karen[m1]-=pk,ntz[m2]-=pk;
                ok=ck(pk,m1+1,c1+(pk==3),c2+(pk==4));
                karen[m1]+=pk,ntz[m2]+=pk;
                if(ok) return 1;
                ++m2;
                while(m2<=13&&ntz[m2]<pk) ++m2;
            }
            ++m1;
            while(m1<=13&&karen[m1]<pk) ++m1;
        }
        ls=0;
    }
    if(!c1) return 0;
    m1=13,m2=0;
    while((~m1)&&karen[m1]<pk) --m1;
    while(m2<=13&&ntz[m2]<pk) ++m2;
    if(m1<0||m2>13) return 0;
    karen[m1]-=pk,ntz[m2]-=pk;
    ok=ck(pk,ls,c1-1,c2);
    karen[m1]+=pk,ntz[m2]+=pk;
    return ok;
}
void dfs(int o,int nm)
{
    if(nm==17)
    {
        if(ck(4,0,0,0)) ++ans;
        return;
    }
    if(o>13) return;
    for(int i=0;i<=lm[o]&&nm+i<=17;++i)
    {
        karen[o]=i;
        dfs(o+1,nm+i);
    }
    karen[o]=0;
}

int main()
{
    inii();
    scanf("%s",cc+1);
    for(int i=1;i<=17;++i) ++ntz[mpp[cc[i]]],--lm[mpp[cc[i]]];
    dfs(0,0);
    printf("%d\n",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章