[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\n",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\n",i,cnt[i]);
        search(0);
        printf("%d\n",ans);
    }
    return 0;
}
posted @ 2018-10-27 19:03  风浔凌  阅读(266)  评论(0编辑  收藏  举报