• 洛谷 题解 P2540 【斗地主增强版】


    【分析】

    暴力搜顺子,贪心出散牌

    为什么顺子要暴力?

    玩过斗地主的都知道,并不是出越长的顺子越好,如果你有一组手牌,3,4,5,6,7,6,7,8,9,10,你一下把最长的出了去,你会单两张牌,不如出两个顺子,所以顺子要暴力。

    贪心打散牌

    这是核心所在,也是不超时的原因。

    可以先统计一下不同牌个数的组数,然后再出牌,

    那如何打出最优解?

    首先一定要先出四带二,再出三带一,这是很容易想到的,因为四带可以带走两张。

    这样写正的行了吗?

    当然不行,原题的随机数额可以过,增强版的必须考虑拆牌,而且还要考虑王炸的特殊情况,王算单牌,但可以当对出,明确这一点。

    拆牌

    什么时候该拆牌呢,对牌除了四带的情况不能拆,因为拆了可能多打一手牌。

    仔细想想,三张和炸在单牌和对牌很多的时候是不能拆的,拆了就多大。

    当单牌和对牌数的和小于三张和炸的和,这就可以拆了

    因为这时不拆的话没得带,只能单出,如果把三张拆成一单和一对,让其余的三张和炸带走,就会少一步。

    四张的同理。

    具体贪心过程(按优先级排序)

    • 把一个炸拆成3张和单牌,再出一组四带二单和三带一

    • 把一组三张拆成一对和一单,再出一组四带二单和三带二

    • 三四张的比单牌和对牌多,拆着打

    • 还多继续拆

    • 四带两单

    • 四带两对

    • 对看成两单再四带

    • 三张的太多了拆三张

    • 把一组三张拆成单和对,再出三带一和三带二

    • 三带一

    • 三带二

    还剩三张和炸,组合出

    • 把两个三张拆成两个对和两个单,再出四带两对和四带两单

    • 把一个炸拆成一对和两单,再出三带二和四带两单

    • 把一个炸拆成两对,再出两组三带一对

    • 同上,把一组三张拆成单和对,再出三带一和三带二

    • 把一个炸拆成两对,再出一组四带两对

    • 有双王一块出

    • 出单牌

    【代码】

    #include<bits/stdc++.h>
    using namespace std;
    int T,n;
    int pai[15];
    int ans=0x3f3f3f3f;
    inline int read()
    {
        int tot=0,f=1;char c=getchar();
        while(c<'0'||c>'9')
        {
            if(c=='-')f=-1;
            c=getchar();
        }
        while(c>='0'&&c<='9')
        {
            tot=tot*10+c-'0';
            c=getchar();
        }
        return tot*f;
    }
    inline bool check()
    {
        for(int i=1;i<=14;i++)
            if(pai[i])return false;
        return true;
    }
    inline int sp()
    {
        bool flag=(pai[14]==2?1:0);
        int c[5],tot=0;
        memset(c,0,sizeof(c));
        c[1]+=pai[14];
        for(int i=1;i<=13;i++)c[pai[i]]++;
        /*for(int i=1;i<=4;i++)
        	cout<<c[i]<<" ";
        cout<<endl;*/
        while(c[3]==0&&c[1]==1&&c[2]==1&&c[4]>1)tot+=2,c[4]-=2,c[1]--,c[2]--;
        while(c[2]==0&&c[1]==1&&c[4]==1&&c[3]>1)tot+=2,c[3]-=2,c[1]--,c[4]--;
        if(c[3]+c[4]>c[1]+c[2])while(c[4]>0&&c[2]>0&&c[3]>0)tot++,c[2]--,c[3]--,c[1]++,c[4]--;
        if(c[3]+c[4]>c[1]+c[2])while(c[4]>0&&c[1]>0&&c[3]>0)tot++,c[1]--,c[3]--,c[2]++,c[4]--;
        while(c[4]>0&&c[1]>1)tot++,c[4]--,c[1]-=2;
        while(c[4]>0&&c[2]>1)tot++,c[4]--,c[2]-=2;
        while(c[4]>0&&c[2]>0)tot++,c[4]--,c[2]--;
        if(c[3]%3==0&&c[1]+c[2]<=1)while(c[3]>2)tot+=2,c[3]-=3;
        while(c[3]>0&&c[2]>0)tot++,c[3]--,c[2]--;
        while(c[3]>0&&c[1]>0)tot++,c[3]--,c[1]--;
        while(c[4]>1&&c[3]>1)tot+=2,c[3]-=2,c[4]-=2;
        while(c[4]>1&&c[3]>0)tot+=2,c[3]--,c[4]-=2;
        while(c[3]>1&&c[4]>0)tot+=2,c[4]--,c[3]-=2;
        while(c[4]>1)tot++,c[4]-=2;
        while(c[3]>2)tot+=2,c[3]-=3;
        /*for(int i=1;i<=4;i++)
        	cout<<c[i]<<" ";
        cout<<endl;*/
        if(c[1]>=2&&flag)return tot+c[2]+c[3]+c[4]+c[1]-1;
        else return tot+c[1]+c[2]+c[3]+c[4];
    }
    inline void dfs(int cnt)
    {
        if(cnt>=ans)return;
        if(check())
        {
            ans=min(ans,cnt);
        }
        int temp=sp();
        /*for(int i=1;i<=14;i++)
            cout<<pai[i]<<" ";cout<<endl; */
        ans=min(temp+cnt,ans);
        for(int i=1;i<=12;i++)
        {
            if(pai[i]<3)continue;
            for(int j=i+1;j<=12;j++)
            {
                if(pai[j]<3)break;
                if(j-i+1>=2)
                {
                    for(int k=i;k<=j;k++)pai[k]-=3;
                    dfs(cnt+1);
                    for(int k=i;k<=j;k++)pai[k]+=3;
                }
            }
        }
        for(int i=1;i<=12;i++)
        {
            if(pai[i]<2)continue;
            for(int j=i+1;j<=12;j++)
            {
                if(pai[j]<2)break;
                if(j-i+1>=3)
                {
                    for(int k=i;k<=j;k++)pai[k]-=2;
                    dfs(cnt+1);
                    for(int k=i;k<=j;k++)pai[k]+=2;
                }
            }
        }
        for(int i=1;i<=12;i++)
        {
            if(!pai[i])continue;
            for(int j=i+1;j<=12;j++)
            {
                if(!pai[j])break;
                if(j-i+1>=5)
                {
                    for(int k=i;k<=j;k++)pai[k]--;
                    dfs(cnt+1);
                    for(int k=i;k<=j;k++)pai[k]++;
                }
            }
        }
    }
    int main()
    {
        //freopen("testdata.in","r",stdin);
        T=read();n=read();
        while(T--)
        {
            memset(pai,0,sizeof(pai));
            ans=0x3f3f3f3f;
            int ds,hs;
            for(int i=1;i<=n;i++)
            {
                ds=read();hs=read();
                if(ds>=3&&ds<=13)pai[ds-2]++;
                else if(ds==0&&hs==1)pai[14]++;
                else if(ds==0&&hs==2)pai[14]++;
                else pai[ds+11]++;
            }
            dfs(0);
            cout<<ans<<endl;
        }
        return 0;
    }
    

    参考博客:https://www.luogu.org/blog/user40673/solution-p2540

  • 相关阅读:
    IE9以上获取元素的写法
    struts2动态跳转action,修改和添加共用一个页面
    Struts2网页面传值两种方式
    hibernate方法中参数传入数组的查询方法
    获取当前泛型类的传入,BaseDaoImpl<T> implements BaseDao<T>
    .net 必看书籍1
    knockjs
    javascript 处理链接的多种方式
    ckeditor 工具栏的配置
    C# 针对文件夹的操作
  • 原文地址:https://www.cnblogs.com/hulean/p/11144064.html
Copyright © 2020-2023  润新知