NOIP 2015 D1 T3 斗地主(特详细讲解)

牛牛最近迷上了一种叫斗地主的扑克游戏。斗地主是一种使用黑桃、红心、梅花、方片的A到K加上大小王的共54张牌来进行的扑克牌游戏。在斗地主中,牌的大小关 系根据牌的数码表示如下:3<4<5<6<7<8<9<10< J< Q< K< A<2<小王<大王 而花色并不对牌的大小产生影响。每一局游戏中,一副手牌由 n 张牌组成。游戏者每次可以根据规定的牌型进行出牌,首先打光自己的手牌一方取得游戏的胜利。

现在,牛牛只想知道,对于自己的若干组手牌,分别最少需要多少次出牌可以将它们打光。请你帮他解决这个问题。

需要注意的是,本题中游戏者每次可以出手的牌型与一般的斗地主相似而略有不同。具体规则如下:
NOIP 2015 D1 T3 斗地主(特详细讲解)_第1张图片

输入格式
第一行包含用空格隔开的2个正整数 T,n表示手牌的组数以及每组手牌的张数。

接下来 T组数据,每组数据 n 行,每行一个非负整数对 ai,bi,表示一张牌,其中 ai表示牌的数码, bi 表示牌的花色,中间用空格隔开。特别的,我们用 11 来表示数码 A, 11 表示数码 J, 12 表示数码 Q, 13 表示数码 K;黑桃、红心、梅花、方片分别用 1-4 来表示;小王的表示方法为 0 1 ,大王的表示方法为 0 2 。

输出格式
共 T 行,每行一个整数,表示打光第 i 组手牌的最少次数。

这绝对不是一个爆搜就完了!这绝对不是一个爆搜就完了!这绝对不是一个爆搜就完了!重说三。

首先….这个题可以是个爆搜或者DP,反正DP我不会。
这个题DFS BFS都可以…..昨天下午我刚了一下午+半晚上….370行最后以30分滚粗….

那么…这个毒瘤爆搜….该怎么做呢?

嗯,来战吧!

首先,这个题我们可以选择用DFS+BFS的结合,因为用BFS的话每次扔十几张牌…爆了队列怎么办啊…DFS则有可能重复搜到之前的状态。

我选择把A,2,王设置成,14,15,,16

其实我今天学到的做法是DFS+状压判重。

把DFS搜到的每一个状态,压缩成一个整数,存到map里面。
233333这是不是利用了hash的思想?

我们读入的时候,开一个pk数组,记录一下每张牌有多少张,大王小王算同一张。

进了DFS,把每一个状态压缩成一个数字储存在map里。

对于每一种出牌方式,我们怎么搞呢?

炸弹&&对子牌&&王炸&&三张 :
for一遍,扫到4/3/2张(及以上)的就把这个位置的牌-4/3/2,进入下一层DFS,然后回溯+4/3/2。

三带一 三张码数相同的牌 + 一张单牌:
for一遍。扫到3张及以上的,当前位置-3,再加一层for,如果这个位置有牌就–。进DFS然后回溯。

三带二 三张码数相同的牌 + 一对牌:
for一遍。扫到3张及以上的,当前位置-3,再加一层for,如果这个位置牌大于等于2就-2。进DFS然后回溯。

四带二 四张码数相同的牌+任意两张单牌(或任意两对牌):
for一遍。扫到四张的,当前位置-4,加一层for,扫到一个位置有2/1及以上的就-2/1,再加一层for,对应扫到一个位置有2/1及以上的就-2/1,然后回溯。

单顺子 五张或更多码数连续的单牌(不包括 2 点和双王):
for的时候要注意2和王,如果当前往后面4个位置都有牌,就说明连成顺子,从这个位置往后搜,如果没有牌直接break,有牌进DFS,记录当前位置,
一次结束的时候从进入位置到当前位置++。

双顺子 三对或更多码数连续的对牌(不包括 2 点和双王)
同上。
三顺子 二个或更多码数连续的三张牌(不能包括 2 点和双王)
同上。

#include
#include
#include
#include
#define fk puts("hahahs"); 
#define mem(a) memset(a,0,sizeof(a));
using namespace std;
typedef unsigned long long ll;
const int maxn=50;
int n;
int pk[maxn];
mapint>vis;

ll ys()
{
    ll hah=0;
    for(int i=3;i<=16;i++)
        hah=hah*6+pk[i];
    return hah;
}
int dfs()
{
    int ans=30;
    ll zt=ys();
    if(zt==0)
        return 0;
    if(vis.count(zt))
        return vis[zt];
    bool flag=0;
    //三顺子 
    for(int i=3;i<=13;i++)
        if(pk[i]>=3&pk[i+1]>=3)
        {
            pk[i]-=3,pk[i+1]-=3;
            ans=min(ans,dfs()+1);
            flag=1;
            int j;
            for(j=i+2;j<=14;j++)
            {
                if(pk[j]<3)
                {   
                    break;
                } 
                else
                {
                    pk[j]-=3;
                    ans=min(ans,dfs()+1);
                }               
            }
            for(int k=i;k3;
        }
    //双顺子 
    for(int i=3;i<=12;i++)
        if(pk[i]>=2&&pk[i+1]>=2&&pk[i+2]>=2)
        {
            pk[i]-=2;pk[i+1]-=2;pk[i+2]-=2;
            flag=1;
            ans=min(ans,dfs()+1);
            int j;
            for(j=i+3;j<=14;j++)
            {
                if(pk[j]<2)
                {
                    break;
                }
                else
                {
                    pk[j]-=2;
                    ans=min(ans,dfs()+1);
                }

            }
            for(int k=i;k2;
        }
    //单顺子 
    for(int i=3;i<=10;i++)
        if(pk[i]&&pk[i+1]&&pk[i+2]&&pk[i+3]&&pk[i+4])
        {
            pk[i]--;pk[i+1]--;pk[i+2]--;pk[i+3]--;pk[i+4]--;
            flag=1;
            ans=min(ans,dfs()+1);
            int j;
            for(j=i+5;j<=14;j++)
            {
                if(!pk[j])
                    break;
                else
                {
                    pk[j]--;
                    ans=min(ans,dfs()+1);
                } 
            }
            for(int k=i;k//四带二 
    for(int i=3;i<16;i++)
    if(pk[i]==4)
    {
        pk[i]-=4;
        for(int j=3;j<16;j++)
        {
            if(pk[j]>=2)
            {
                pk[j]-=2;
                for(int k=3;k<16;k++)
                {
                    if(pk[k]>=2)
                    {
                        pk[k]-=2;
                        flag=1;
                        ans=min(ans,dfs()+1);
                        pk[k]+=2;
                    }
                }
                pk[j]+=2;
            }
        }//一对 
        for(int j=3;j<=16;j++)
        {
            if(pk[j])
            {
                pk[j]--;
                for(int k=3;k<=16;k++)
                {
                    if(pk[k])
                    {
                        pk[k]--;
                        flag=1;
                        ans=min(ans,dfs()+1);
                        pk[k]++;
                    }
                }
                pk[j]++;
            }
        }//俩单 

        pk[i]+=4;
    }

    //三带二 
    for(int i=3;i<16;i++)
    if(pk[i]>=3)
    {
        pk[i]-=3;
        for(int j=3;j<=16;j++)
        {
            if(pk[j]>=2)
            {
                flag=1;
                pk[j]-=2;
                ans=min(ans,dfs()+1);
                pk[j]+=2;
            }
        }
        pk[i]+=3;
    }
    //三带一
    for(int i=3;i<16;i++)
    if(pk[i]>=3)
    {
        pk[i]-=3;
        for(int j=3;j<=16;j++)
        {
            if(pk[j])
            {
                flag=1;
                pk[j]--;
                ans=min(ans,dfs()+1);
                pk[j]++;
            }
        }
        pk[i]+=3;
    }
    //炸弹 
    for(int i=3;i<16;i++)
    if(pk[i]==4)
    {
        pk[i]-=4;
        ans=min(ans,dfs()+1);
            flag=1;
        pk[i]+=4;
    }
    //三单
    for(int i=3;i<16;i++)
    if(pk[i]>=3)
    {
        pk[i]-=3;
        ans=min(ans,dfs()+1);
            flag=1;
        pk[i]+=3;
    }

    //对牌
    for(int i=3;i<=16;i++)
    {
        if(pk[i]>=2)
        {
            pk[i]-=2;
            ans=min(ans,dfs()+1);
                flag=1;
            pk[i]+=2;
        }
    }   

    //单牌 
    if(!flag)
    {
        ans=0;
        for(int i=3;i<=16;i++)
            ans+=pk[i]; 
    }
    vis[zt]=ans;
//      printf("%d\n",ans);
    return ans; 
}

int main()
{
    freopen("landlords.in","r",stdin);  
    freopen("landlords.out","w",stdout);
    int t;
    scanf("%d%d",&t,&n);
    while(t--)
    {
        mem(pk);
        for(int i=1;i<=n;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            if(!a)
                pk[16]++;
            else if(a>=3)
                pk[a]++;
            else
                pk[a+13]++;
        }
        printf("%d\n",dfs());
        vis.clear();
    }
    return 0;
}

你可能感兴趣的:(===搜索===,DFS,BFS)