NOIP2015斗地主

版权声明:欢迎转载(标记出处),写得不好还请多指教 https://blog.csdn.net/quan_tum/article/details/82558273

表示本来觉得自己斗地主玩得挺好的也挺喜欢的做了这题之后对于斗地主的看法完全改变了(原来可以这样拆牌?)。
做这题之前就听见别人说暴力搜顺子贪心出散牌,所以就是每一层搜下顺子,再计算一下打出剩下牌的最小次数,其实主要还是模拟。

#include<bits/stdc++.h>
#define il inline
#define ll long long
using namespace std;
#define getchar()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
il int read(){
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=(x+(x<<2)<<1)+c-48;
    return x*f;
}
char sr[1<<21],z[20];int C=-1,Z;
il void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
il void print(ll x){
    if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
int T,n,ans,a[15],cd[15],sz[4]={0,5,3,2};
il int calc(){
    memset(a,0,sizeof(a));
    for(int i=0;i<=13;++i) ++a[cd[i]];
    int tot=0;
    while(a[4]&&a[2]>=2) --a[4],a[2]-=2,++tot;
    while(a[4]&&a[1]>=2) --a[4],a[1]-=2,++tot;
    while(a[4]&&a[2]) --a[4],--a[2],++tot;
    while(a[3]&&a[2]) --a[3],--a[2],++tot;
    while(a[3]&&a[1]) --a[3],--a[1],++tot;
    return tot+a[1]+a[2]+a[3]+a[4];
}
il int shunzi(int i,int x){
    int p=i;
    while(cd[p]>=x&&p<=13) ++p;
    if(p-i>=sz[x]) return p;
    return 0;
}
il void dfs(int s){
    if(s>=ans) return;
    ans=min(ans,s+calc());
    int pos;
    for(int k=3;k;--k)
    for(int i=2;i<=13;++i)
    if(shunzi(i,k)){
        pos=shunzi(i,k);
        for(int j=i+sz[k]-1;j<=pos-1;++j){
            for(int v=i;v<=j;++v) cd[v]-=k;
            dfs(s+1);
            for(int v=i;v<=j;++v) cd[v]+=k;
        }
    }
}
int main(){
    T=read(),n=read();
    while(T--){
        ans=33;
        memset(cd,0,sizeof(cd));
        for(int i=1;i<=n;++i){
            int x=read(),y=read();
            if(x==1) x=13;
            else if(x) --x;
            ++cd[x];
        }
        dfs(0);print(ans);
    }Ot();return 0;
}

猜你喜欢

转载自blog.csdn.net/quan_tum/article/details/82558273
今日推荐