斗地主题解

· · 个人记录

由于本人特别会打斗地主,所以此题我在普通版本的代码稍加修改(也就是大小王不能当对这处),然后通过了加强版。

我没有考虑怎么贪心甚至一开始我都不知道从哪下手开始搜索,那先考虑暴力搜,也就是每张牌的出牌形式。

剪枝:

其实我们发现第一种是核心剪枝必须有序才行。

第二,三种是必要的剪枝。

还有一点需要注意的是发现前面的单牌可以和后面的一些牌进行操作我们必须考虑到,这并不浪费时间。

这样我们顺利通过了这道题不用贪心啦,拆牌的考虑辣么多。

//#include<bits/stdc++.h>
#include<iostream>
#include<queue>
#include<iomanip>
#include<cctype>
#include<cstdio>
#include<deque>
#include<utility>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cstdlib>
#include<vector>
#include<algorithm>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
#define INF 10000000000000ll
#define ll long long
#define l(x) t[x].l
#define r(x) t[x].r
#define v(x) t[x].v
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline ll read()
{
    ll x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
inline void put(ll x)
{
    x<0?putchar('-'),x=-x:0;
    ll num=0;char ch[50];
    while(x)ch[++num]=x%10+'0',x/=10;
    num==0?putchar('0'):0;
    while(num)putchar(ch[num--]);
    putchar('\n');return;
}
//1 最优化剪枝 2 按顺序剪枝
const int MAXN=30;
int T,n;
int maxx=25,m;
int a[MAXN];
inline void dfs(int x,int depth,int p)
{
    if(depth>=maxx)return;
    if(p&&depth+1==maxx)return;
    if(!p){maxx=min(maxx,depth);return;}
    if(!a[x]){dfs(x+1,depth,p);return;}
    --a[x];
    for(int i=x+1;i<=14;++i)//被动三带一
        if(a[i]>2)
        {
            a[i]-=3;
            dfs(x,depth+1,p-4);
            a[i]+=3;
        }
    int flag=0;
    for(int i=x+1;i<=14;++i)if(a[i]>3)flag=i;
    if(flag)
    {
        a[flag]-=4;
        for(int i=x;i<=15;++i)//被动4带二
            if(a[i])
            {
                --a[i];
                dfs(x,depth+1,p-6);
                ++a[i];
            }
        a[flag]+=4;
    }
    dfs(x,depth+1,p-1);//出单张牌
    ++a[x];
    if(x!=2)if(a[x+1]&&a[x+2]&&a[x+3]&&a[x+4]&&x+4<=14)
    {
        --a[x];--a[x+1];--a[x+2];--a[x+3];--a[x+4];
        dfs(x,depth+1,p-5);
        int w=x+5;
        while(a[w]&&w<=14)//单顺子
        {
            --a[w];
            dfs(x,depth+1,p-(w-x+1));
            ++w;
        }
        for(int i=w-1;i>=x;--i)++a[i];
    }
    if(a[x]>1)//出对子 或者火箭
    {
        a[x]-=2;
        dfs(x,depth+1,p-2);
        int flag=0;
        for(int i=x+1;i<=14;++i)if(a[i]>3)flag=i;
        if(flag)
        {
            a[flag]-=4;
            for(int i=x+1;i<=14;++i)
                if(a[i]>1)
                {
                    a[i]-=2;
                    dfs(x,depth+1,p-8);
                    a[i]+=2;
                }
            a[flag]+=4;//被动四代二
        }
        for(int i=x+1;i<=14;++i)//被动三带二
            if(a[i]>2)
            {
                a[i]-=3;
                dfs(x,depth+1,p-5);
                a[i]+=3;
            }
        a[x]+=2;
        if(x!=2&&a[x+1]>1&&a[x+2]>1&&x+2<=14)
        {
            a[x]-=2;a[x+1]-=2;a[x+2]-=2;
            int w=x+3;//注意不要加错
            dfs(x,depth+1,p-6);
            while(a[w]>1&&w<=14)//双顺子 
            {
                a[w]-=2;
                dfs(x,depth+1,p-(w-x+1)*2);
                ++w;
            }
            for(int i=w-1;i>=x;--i)a[i]+=2;
        }
    }
    if(a[x]>2)
    {
        a[x]-=3;
        dfs(x,depth+1,p-3);//出三张牌
        for(int i=x+1;i<=15;++i)
        {   
            if(a[i])
            {
                --a[i];
                dfs(x,depth+1,p-4);//三带一
                ++a[i];
            }
            if(a[i]>1&&i<=14)
            {
                a[i]-=2;
                dfs(x,depth+1,p-5);//三带二
                a[i]+=2;
            }
        }
        a[x]+=3;
        if(a[x+1]>2&&x!=2)//三顺子
            {
                int w=x+2;//注意两个即可
                a[x]-=3;a[x+1]-=3;
                dfs(x,depth+1,p-6);
                while(a[w]>2)
                {
                    a[w]-=3;
                    dfs(x,depth+1,p-(w-x+1)*3);
                    ++w;
                }
                for(int j=w-1;j>=x;--j)a[j]+=3;
            }
    }
    if(a[x]>3)
    {
        a[x]-=4;
        dfs(x+1,depth+1,p-4);//炸弹
        for(int i=x+1;i<=15;++i)//终极四带二
            for(int j=i;j<=15;++j)
            {
                if(i==j)//注意相等
                {
                    if(a[i]>1)
                    {
                        a[i]-=2;
                        dfs(x+1,depth+1,p-6);
                        a[i]+=2;
                    }
                    if(a[i]>3)
                    {
                        a[i]-=4;
                        dfs(x+1,depth+1,p-8);
                        a[i]+=4;
                    }
                    continue;
                }
                if(a[i]&&a[j])
                {
                    --a[i];--a[j];
                    dfs(x+1,depth+1,p-6);
                    ++a[i];++a[j];
                }
                if(a[i]>1&&a[j]>1&&i!=15&&j!=15)
                {
                    a[i]-=2;a[j]-=2;
                    dfs(x+1,depth+1,p-8);
                    a[i]+=2;a[j]+=2;
                }
            }
        a[x]+=4;
    }
    return;
}
int main()
{
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    T=read();n=read();
    while(T--)
    {
        memset(a,0,sizeof(a));maxx=25;
        for(int i=1;i<=n;++i)
        {
            int x,y;
            x=read();y=read();
            if(x==0&&y==1){++a[15];continue;}
            if(x==0&&y==2){++a[15];continue;}
            if(x==1){++a[14];continue;}
            ++a[x];
        }
        //for(int i=2;i<=15;++i)cout<<a[i]<<endl;
        dfs(2,0,n);
        put(maxx);
    }
    return 0;
}