斗地主

发布时间 2023-10-07 20:12:21作者: wscqwq

P2668 [NOIP2015 提高组] 斗地主

我们发现,除了顺子以外,其他的出牌方式和大小无关,我们先爆搜所有的顺子,搜索完之后剩下的牌我们考虑不用顺子,用其他牌型。

此时我们关心的只有单牌、对子、三个、炸弹四种,每种分别最多 \(23,11,7,5\) 个,状态很少,记忆化即可。

注意加强版需要额外一维表示是否有双王,双王可以当对子出,但是不能四带二;但是可以三带一,我们可以特殊判断,如果只剩一张王,把它划入普通单牌的行列。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define Ed for(int i=h[x];~i;i=ne[i])
#define Ls(i,l,r) for(int i=l;i<r;++i)
#define Rs(i,l,r) for(int i=l;i>r;--i)
#define Le(i,l,r) for(int i=l;i<=r;++i)
#define Re(i,l,r) for(int i=l;i>=r;--i)
#define L(i,l) for(int i=0;i<l;++i)
#define E(i,l) for(int i=1;i<=l;++i)
#define W(t) while(t--)
#define Wh while

const int N=1,INF=100,len[]={0,5,3,2};
int T,n,f[24][12][8][6];
int calc(int s[]){
    E(i, 4)
        if(s[i]<0)return INF;
    int sum=0;
    E(i, 4)sum+=s[i];
    int &v=f[s[1]][s[2]][s[3]][s[4]];
    if(~v)return v;
    if(!sum)return v=0;
    v=INF;
    E(i, 4)
        Le(k, i, 4){
            --s[k],++s[k-i];
            v=min(v,calc(s)+1);
            if(i==3){
                Le(x, 1, 2)
                    Le(y, x, 4){
                        --s[y],++s[y-x];
                        v=min(v,calc(s)+1);
                        ++s[y],--s[y-x];
                    }
            }
            if(i==4){
                Le(x, 1, 2)
                    Le(y, x, 4)
                        Le(z, x, 4){
                            --s[y],++s[y-x];
                            --s[z],++s[z-x];
                            v=min(v,calc(s)+1);
                            ++s[y],--s[y-x];
                            ++s[z],--s[z-x];
                        }
            }
            ++s[k],--s[k-i];
        }
    return v;
}
void add(int a[],int l,int r,int k){
    Le(i, l, r)
        a[i]+=k;
}
bool chk(int a[],int l,int r,int k){
    Le(i, l, r)
        if(a[i]<k)return false;
    return true;
}
int dfs(int cnt[]){
    L(i, 14)
        if(cnt[i]<0)return INF;
    int tmp[5]={};
    L(i, 14)
        ++tmp[cnt[i]];
    int ans=calc(tmp);
    E(i, 3)
        Le(l, 2, 13)
            Le(r, l+1, 13){
                if(r-l+1<len[i]||!chk(cnt,l,r,i))continue;
                add(cnt,l,r,-i);
                ans=min(ans,dfs(cnt)+1);
                add(cnt,l,r,i);
            }
    return ans;
}
int main(){
    #ifndef ONLINE_JUDGE
    freopen("1.in","r",stdin);
    #endif
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>T>>n;
    memset(f,-1,sizeof f);
    W(T){
        int cnt[14]{};
        E(i, n){
            int a,b;
            cin>>a>>b;
            if(a==1)a=13;
            else if(a)a--;
            ++cnt[a];
        }
        cout<<dfs(cnt)<<'\n';
    }
    return 0;
}

加强版

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define Ed for(int i=h[x];~i;i=ne[i])
#define Ls(i,l,r) for(int i=l;i<r;++i)
#define Rs(i,l,r) for(int i=l;i>r;--i)
#define Le(i,l,r) for(int i=l;i<=r;++i)
#define Re(i,l,r) for(int i=l;i>=r;--i)
#define L(i,l) for(int i=0;i<l;++i)
#define E(i,l) for(int i=1;i<=l;++i)
#define W(t) while(t--)
#define Wh while

const int N=1,INF=100,len[]={0,5,3,2};
int T,n,f[24][12][8][6][2];
bool two_king;
int calc(int s[],bool two){
    E(i, 4)
        if(s[i]<0)return INF;
    int sum=0;
    E(i, 4)sum+=s[i];
    int &v=f[s[1]][s[2]][s[3]][s[4]][two];
    if(~v)return v;
    if(!sum&&!two)return v=0;
    v=INF;
    if(two)v=min(v,calc(s,0)+1);
    E(i, 4)
        Le(k, i, 4){
            --s[k],++s[k-i];
            v=min(v,calc(s,two)+1);
            if(i==3){
                Le(x, 1, 2)
                    Le(y, x, 4){
                        --s[y],++s[y-x];
                        v=min(v,calc(s,two)+1);
                        ++s[y],--s[y-x];
                    }
                ++s[1];
                v=min(v,calc(s,two-1)+1);
                --s[1];
            }
            if(i==4){
                Le(x, 1, 2)
                    Le(y, x, 4)
                        Le(z, x, 4){
                            --s[y],++s[y-x];
                            --s[z],++s[z-x];
                            v=min(v,calc(s,two)+1);
                            ++s[y],--s[y-x];
                            ++s[z],--s[z-x];
                        }
            }
            ++s[k],--s[k-i];
        }
    return v;
}
void add(int a[],int l,int r,int k){
    Le(i, l, r)
        a[i]+=k;
}
bool chk(int a[],int l,int r,int k){
    Le(i, l, r)
        if(a[i]<k)return false;
    return true;
}
int dfs(int cnt[]){
    int tmp[5]={},two=0;
    L(i, 14)
        if(!i)two=cnt[i]==2;
        else ++tmp[cnt[i]];
    if(!two)++tmp[cnt[0]];
    int ans=calc(tmp,two);
    E(i, 3)
        Le(l, 2, 13)
            Le(r, l+1, 13){
                if(r-l+1<len[i]||!chk(cnt,l,r,i))continue;
                add(cnt,l,r,-i);
                ans=min(ans,dfs(cnt)+1);
                add(cnt,l,r,i);
            }
    return ans;
}
int main(){
    #ifndef ONLINE_JUDGE
    freopen("1.in","r",stdin);
    #endif
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>T>>n;
    memset(f,-1,sizeof f);
    W(T){
        int cnt[14]{};
        E(i, n){
            int a,b;
            cin>>a>>b;
            if(a==1)a=13;
            else if(a)a--;
            ++cnt[a];
        }
        two_king=cnt[0]==2;
        cout<<dfs(cnt)<<'\n';
    }
    return 0;
}