bzoj 4325: NOIP2015 斗地主 (dfs+剪枝)

4325: NOIP2015 斗地主

Time Limit: 30 Sec   Memory Limit: 1024 MB
Submit: 638   Solved: 424
[ Submit][ Status][ Discuss]

Description

 牛牛最近迷上了一种叫斗地主的扑克游戏。斗地主是一种使用黑桃、红心、梅花、方片的A到K加上大小王的共54张牌来进行的扑克牌游戏。在斗地主中,牌的大小关系根据牌的数码表示如下:3<4<5<6<7<8<9<10<J<Q<K<A<2<小王<大王,而花色并不对牌的大小产生影响。每一局游戏中,一副手牌由n张牌组成。游戏者每次可以根据规定的牌型进行出牌,首先打光自己的手牌一方取得游戏的胜利。现在,牛牛只想知道,对于自己的若干组手牌,分别最少需要多少次出牌可以将它们打光。请你帮他解决这个问题。需要注意的是,本题中游戏者每次可以出手的牌型与一般的斗地主相似而略有不同。具体规则如下:

Input

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

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

Output

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

Sample Input

1 8
7 4
8 4
9 1
10 4
11 1
5 1
1 4
1 1

Sample Output

3

HINT

 共有1组手牌,包含8张牌:方片7,方片8,黑桃9,方片10,黑桃J,黑桃5,方


片A以及黑桃A。可以通过打单顺子(方片7,方片8,黑桃9,方片10,黑桃J),单张

牌(黑桃5)以及对子牌(黑桃A以及方片A)在3次内打光。


T<=10

N<=23

Source

[ Submit][ Status][ Discuss]
题解:dfs+剪枝。

一定要读题,不要根据自己打牌的习惯想当然。。。。

就是每次搜索怎么出牌。要加一个最优化剪枝。如果当前还有k种牌,那么最多还会出k次。

还有就是单出的情况,不要搜索,直接用上面的最优化剪枝就可以得到这种答案。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define N 100
using namespace std;
int n,m,num[N],ans;
int ke[10],step[N];
int pd()
{
    for (int i=1;i<=15;i++)
     if (num[i]) return 0;
    return 1;
}
void dfs(int x)
{
    if (x>ans) return;
    int t=0;
    for (int i=1;i<=13;i++)  
     if (num[i]) t++;
    if (num[14]+num[15]) t++;
    ans=min(ans,x-1+t);
    if (pd()) {
        ans=min(ans,x-1);
        return;
    }
    int l=1; int r=1; int sum=0;
    for (int i=1;i<=3;i++)
    {
        step[x]=i+4;
        l=1; r=1;
        while (l<=13) {
            while (num[l]<i&&l<=13) l++,r++;
            while (num[r]>=i&&r<=13) r++;
            r--;
            if (r<l) r=l;
            if (r==13) r--;
            int t=r-l+1;
            if (t>=ke[i]) {
                for (int t=l;t<=r-ke[i]+1;t++)
                 for (int j=l+ke[i]-1;j<=r;j++)
                  {
                    if (j-t+1<ke[i]) continue;
                    for (int k=t;k<=j;k++) num[k]-=i;
                    dfs(x+1);
                    for (int k=t;k<=j;k++) num[k]+=i;
                  }
            }
            l=r; 
            l++; r++;
            if (l==13)   break;
        }
    }
    for (int i=1;i<=13;i++)
     if (num[i]>=3) {
        step[x]=2;
        num[i]-=3; 
        for (int j=1;j<=15;j++)
        {
         if (num[j]>=1) {
            num[j]--;
            dfs(x+1);
            num[j]++;
          }
         if (num[j]>=2) {
            num[j]-=2; 
            dfs(x+1);
            num[j]+=2;
         }
        }
        num[i]+=3;
     }
    for (int i=1;i<=13;i++)
     if (num[i]==4) {
        step[x]=3;
        num[i]-=4;
        for (int t=1;t<=2;t++){
            for (int j=1;j<=15;j++)
             if (num[j]>=t) {
                num[j]-=t;
                for (int k=j;k<=15;k++)
                 if (num[k]>=t) {
                    num[k]-=t;
                    dfs(x+1);
                    num[k]+=t;
                   }
                num[j]+=t;
              }
        }
        num[i]+=4;
     }
}
int main()
{
    scanf("%d%d",&n,&m);
    ke[1]=5; ke[2]=3; ke[3]=2;
    for (int i=1;i<=n;i++) {
        memset(num,0,sizeof(num));
        int size=0;
        for (int j=1;j<=m;j++) {
            int x,y; scanf("%d%d",&x,&y);
            if (x){
             if (x>=3&&x<=13) num[x-2]++;
             else num[x+11]++;
            }
            else num[y+13]++;
        }
        ans=0;
        for (int i=0;i<=13;i++)
         if (num[i]) ans++;
        if (num[14]+num[15]) ans++;
        dfs(1);
        printf("%d\n",ans);
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值