zoukankan      html  css  js  c++  java
  • [NOIP2015] 斗地主

    这是个很繁琐的贪心+模拟题。但是好在NOIP是随机数据,所以数据强度不是很大qwq

    做题的时候我们注意一些细节就可以了。

    • 并不是纯模拟+搜索,需要贪心。注意顺序:优先考虑顺子,之后是带牌,最后才是散牌

    • 四带二可以带散牌,也可以带两个对子

    • 有的情况二不能带

    • 有一个优化是最后炸弹,对子,单牌,火箭,三张牌的处理同意按照出牌次数+1处理,因为如果可以出的话前面搜索就搜过了,现在剩下肯定是只能自己出自己的。

    然后就没有了,代码如下:(里面的multiset是调试用的qwq)

    // luogu-judger-enable-o2
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<set>
    #define MAXN 20
    using namespace std;
    int t,ans,n;
    int cnt[MAXN];
    multiset<string>v;
    inline void print()
    {
        printf("ans=%d
    ",ans);
        for(multiset<string>::iterator it=v.begin();it!=v.end();it++)
            cout<<*it<<" ";
        cout<<endl<<endl;
        return;
    }
    inline void search(int sum)
    {
        if(sum>=ans) return;
        for(int i=3;i<=13;i++)//判断三顺子 
        {
            if(cnt[i]>=3&&cnt[i+1]>=3) 
            {
                int pos=i+2;
                while(cnt[pos]>=3&&pos<=14)pos++; pos--; 
                for(int k=i+1;k<=pos;k++)
                {
                    for(int j=i;j<=k;j++)
                        cnt[j]-=3;
                    v.insert("三顺子"),search(sum+1),v.erase("三顺子"); 
                    for(int j=i;j<=k;j++)
                        cnt[j]+=3;
                }
            }
        }
        for(int i=3;i<=12;i++)//判断双顺子
        {
            if(cnt[i]>=2&&cnt[i+1]>=2&&cnt[i+2]>=2)
            {
                int pos=i+3;
                while(cnt[pos]>=2&&pos<=14)pos++; pos--;
                for(int k=i+2;k<=pos;k++)
                {
                    for(int j=i;j<=k;j++)
                        cnt[j]-=2;
                    v.insert("双顺子"),search(sum+1),v.erase("双顺子");
                    for(int j=i;j<=k;j++)
                        cnt[j]+=2;
                }
            }
         }
        for(int i=3;i<=10;i++)//判断单顺子 
        {
            if(cnt[i]>=1&&cnt[i+1]>=1&&cnt[i+2]>=1&&cnt[i+3]>=1&&cnt[i+4]>=1)
         	{
         		int pos=i+5;
         		while(cnt[pos]&&pos<=14)pos++; pos--;
         		for(int k=i+4;k<=pos;k++)
         		{
                 	for(int j=i;j<=k;j++)
         				cnt[j]--;
         	    	v.insert("单顺子"),search(sum+1),v.erase("单顺子");
         			for(int j=i;j<=k;j++)
         				cnt[j]++;
         		}
            }
        }
        for(int i=3;i<=15;i++)//四带二 
        {
            if(cnt[i]>=4)
            {
                for(int j=3;j<=15;j++)//带两个对子 
                {
                    if(i==j||cnt[j]<2) continue;
                    for(int k=3;k<=15;k++)
                    {
                        if(i==k||j==k||cnt[k]<2) continue;
                        cnt[i]-=4,cnt[j]-=2,cnt[k]-=2;
                        v.insert("4-2对子"),search(sum+1),v.erase("4-2对子");
                        cnt[i]+=4,cnt[j]+=2,cnt[k]+=2;
                    }
                }
                for(int j=3;j<=15;j++)//带两张散牌 
                {
                    if(i==j||cnt[j]==0) continue;
                    for(int k=3;k<=15;k++)
                    {
                        if(i==k||j==k||cnt[k]==0) continue;
                        cnt[i]-=4,cnt[j]--,cnt[k]--;
                        v.insert("4-2散牌"),search(sum+1),v.erase("4-2散牌");
                        cnt[i]+=4,cnt[j]++,cnt[k]++;
                    }
                }	
            }	
        }
        for(int i=3;i<=15;i++)
        {
            if(cnt[i]>=3) 
            {
                for(int j=3;j<=15;j++)//3-对子 
                {
                    if(cnt[j]<2||i==j) continue;
                    cnt[i]-=3,cnt[j]-=2;
                    v.insert("3-2"),search(sum+1),v.insert("3-2");
                    cnt[i]+=3,cnt[j]+=2;
                }
                for(int j=3;j<=16;j++)//3-1
                {
                    if(cnt[j]==0||j==i) continue;
                    cnt[i]-=3,cnt[j]--;
                    v.insert("3-1"),search(sum+1),v.erase("3-1");
                    cnt[i]+=3,cnt[j]++;	
                }	
            }
        }
        for(int i=3;i<=16;i++) if(cnt[i]) sum++;
        ans=min(ans,sum);
        //print();
    }
    int main()
    {
        
        scanf("%d%d",&t,&n);
        while(t--)
        {
            memset(cnt,0,sizeof(cnt));
            ans=0x3f3f3f3f;
            for(int i=1;i<=n;i++)
            {
                int id,color;
                scanf("%d%d",&id,&color);
                if(id==1) cnt[14]++;
                else if(id==2) cnt[15]++;
                else if(id==0) cnt[16]++;
                else cnt[id]++;
            }
            //for(int i=3;i<=15;i++)
            //	printf("cnt[%d]=%d
    ",i,cnt[i]);
            search(0);
            printf("%d
    ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    Ackerman 函数奇怪的函数
    HDU2871 Memory Control 线段树区间合并
    HDU3667 Hotel 线段树 经典空间合并
    图论
    HDU3016 Man Down 线段树
    HDU1878 欧拉回路 判定是否存在欧拉回路
    从今开始
    如何打开注册表编辑器
    ASP.NET中TextBox控件设置ReadOnly="true"后台取不到值
    学习笔记(2011年5月到9月)
  • 原文地址:https://www.cnblogs.com/fengxunling/p/9862768.html
Copyright © 2011-2022 走看看