【NOIP2015】斗地主(搜索,贪心)

题面戳我

题解

我原来也觉得是一道不可做的难题。。
其实,,,很简单的啦。。。
对于当前状态
我们出牌的方式大致分为两类
一类是不用考虑点数的,包括单张,对子,三带一等
另一类就是需要考虑点数的,包括顺子等

因此,每种状态下,首先考虑不用考虑点数的出牌方法
尝试打完,更新打完。
搜索的作用是考虑要考虑点数的出牌方法
每次搜索去掉若干张牌,然后递归,继续考虑不考虑点数的出牌方法。

然后就发现这道题目特别简单啦。。。
(还没懂就看代码吧。。。)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
using namespace std;
inline int read()
{
    int x=0,t=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int ans,N,a[20];
void DFS(int st)
{
    if(st>ans)return;
    int b[5];
    b[1]=b[2]=b[3]=b[4]=0;
    for(int i=1;i<=14;++i)b[a[i]]++;
    //方式1:四张带两张/对
    for(int i=1;i<=14;++i)
        if(a[i]==4)
        {
            if(b[1]>1){b[1]-=2;continue;}//带走两张单牌
            else if(b[2]>1){b[2]-=2;continue;}//带走两对牌
            else if(b[2]){b[2]--;continue;}//只能带走一对
        }
    //方式2:三张带一张/对
    for(int i=1;i<=14;++i)
    {
        if(a[i]==3)
        {
            if(b[1])b[1]--;//带一张
            else if(b[2])b[2]--;//带一对
        }
    }
    ans=min(st+b[1]+b[2]+b[3]+b[4],ans);//上面的出牌方式和ans比较
    //接下来的方式需要考虑牌的点数
    int pos;
    for(int i=1;i<=8;++i)//顺子的开始位置
    {
        pos=12;
        for(int j=i;j<13;++j)//最多打到A
        {
            a[j]--;//打出顺子
            if(a[j]==-1){pos=j;break;}//没有这么多牌呀。。。
            if(j-i>3)DFS(st+1);//打出了一个顺子
        }
        while(pos>=i)a[pos--]++;//回溯
    }
    for(int i=1;i<=10;++i)
    {
        pos=12;
        for(int j=i;j<13;++j)
        {
            a[j]-=2;
            if(a[j]<0){pos=j;break;}
            if(j-i>1)DFS(st+1);//一个双顺子
        }
        while(pos>=i)a[pos--]+=2;
    }
    for(int i=1;i<=11;++i)
    {
        pos=12;
        for(int j=i;j<13;++j)
        {
            a[j]-=3;
            if(a[j]<0){pos=j;break;}
            if(j-i)DFS(st+1);
        }
        while(pos>=i)a[pos--]+=3;
    }
}
int main()
{
    int T=read();N=read();
    while(T--)
    {
        memset(a,0,sizeof(a));
        for(int i=1;i<=N;++i)
        {
            int x=read(),y=read();
            if(x==0)a[14]++;
            else if(x<3)a[x+11]++;
            else a[x-2]++;
        }
        ans=N;
        DFS(0);
        printf("%d
",ans);
    }
    return 0;    
}


原文地址:https://www.cnblogs.com/cjyyb/p/7620436.html