zoukankan      html  css  js  c++  java
  • 洛谷 题解 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

  • 相关阅读:
    vue form dynamic validator All In one
    TypeScript api response interface All In One
    closable VS closeable All In One
    macOS 如何开启 WiFi 热点 All In One
    vue css inline style All In One
    vs2010里面 新建网站里面的 asp.net网站 和 新建项目里面的 asp.net Web应用程序 的区别 (下)
    牛腩新闻 59 整合添加新闻页 FreeTextBox 富文本编辑器,检测到有潜在危险的 Request.Form 值,DropDownList 的使用
    牛腩新闻 61尾声: error.aspx的使用 防止报错
    vs2010里面 新建网站里面的 asp.net网站 和 新建项目里面的 asp.net Web应用程序 的区别 (上)
    牛腩新闻 62:尾声续2 asp.net的编译和发布
  • 原文地址:https://www.cnblogs.com/hulean/p/11144064.html
Copyright © 2011-2022 走看看