斗地主(codevs 4610)

题目描述 Description

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

输入描述 Input Description

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

输出描述 Output Description

输出文件名为 landlords.out。
共 T 行,每行一个整数,表示打光第i组手牌的最少次数。

样例输入 Sample Input

输入样例1

1 8

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

1 1

—————

输入样例2


1 17
12 3
4 3
2 3
5 4
10 2
3 3
12 2
0 1
1 3
10 1
6 2
12 1
11 3

5 2
12 4
2 2
7 2

 

样例输出 Sample Output

输出样例1

3

 

—————

输出样例2

6

数据范围及提示 Data Size & Hint

测试点, 我们约定手牌组数 与张数 的规模如下:
测试点编号  T    n

 

1 100 2 |11 100 14
2 100 2 |12 100 15
3 100 3 |13 10 16
4 100 3 |14 10 17
5 100 4 |15 10 18
6 100 4 |16 10 19
7 100 10 |17 10 20
8 100 11 |18 10 21
9 100 12 |19 10 22

#include<cstdio>  
#include<cstring>  
#include<iostream>
#define M 18
using namespace std;  
int n,x[M],cnt[5],ans;  
int read()
{
    char c=getchar();int num=0;
    while(c<'0'||c>'9'){c=getchar();}
    while(c>='0'&&c<='9'){num=num*10+c-'0';c=getchar();}
    return num;
} 
int calc()//先把能带着出的都出上 
{  
    memset(cnt,0,sizeof(cnt));  
    int res=0;  
    for(int i=0;i<M;i++)cnt[x[i]]++; //cnt[i]表示出现i次的数有几个 
    while(cnt[4]&&cnt[2]>=2) res++,cnt[4]--,cnt[2]-=2; //四带两对 
    while(cnt[4]&&cnt[1]>=2) res++,cnt[4]--,cnt[1]-=2; //四带二 
    while(cnt[3]&&cnt[2]>=1) res++,cnt[3]--,cnt[2]-=1; //三带一对 
    while(cnt[3]&&cnt[1]>=1) res++,cnt[3]--,cnt[1]-=1; //三带一 
    return res+cnt[1]+cnt[2]+cnt[3]+cnt[4]; //统计出当前答案 
}  
inline void dfs(int step) 
{  
    if(step>ans) return;
    //每更新一个状态之前,判断当前状态先出“n带m”是否更优 
    ans=min(ans,step+calc()); 
    //三顺子
    for(int i=2;i<=M;i++)//最小从3开始 
    {  
        int j;
        for(j=i;x[j]>=3;j++);//可以确保 i~j-1 之间所有数字的数量都>=3  
        if(j-i>=2)//至少连续2个 
        {  
            for(int t=j;t-i>=2;t--)//从j枚举结束位置 
            {  
                for(int k=i;k<t;k++) x[k]-=3;  
                dfs(step+1);  
                for(int k=i;k<t;k++) x[k]+=3;//回溯  
            }  
        }  
    }
    //双顺子  
    for(int i=2;i<=M;i++) 
    {  
        int j;
        for(j=i;x[j]>=2;j++);  
        if(j-i>=3) 
        {  
            for(int t=j;t-i>=3;t--) 
            {  
                for(int k=i;k<t;k++) x[k]-=2;  
                dfs(step+1);  
                for(int k=i;k<t;k++) x[k]+=2;  
            }  
        }  
    }
    //单顺子 
    for(int i=2;i<=M;i++) 
    {  
        int j;
        for(j=i;x[j]>=1;j++);  
        if(j-i>=5) 
        {  
            for(int t=j;t-i>=5;t--) 
            {  
                for(int k=i;k<t;k++) x[k]-=1;  
                dfs(step+1);  
                for(int k=i;k<t;k++) x[k]+=1;  
            }  
        }  
    }  
}  
int main() 
{  
    int T=read(),n=read();  
    while(T--) 
    {  
        memset(x,0,sizeof(x));  
        for(int i=1;i<=n;i++) 
        {  
            int w=read(),c=read();  
            if(w==1) w=13;//把A转成13,其余数字减1 
            else if(w) w--;  
            x[w]++;  //统计每个数字的个数 
        }
        ans=calc();  
        dfs(0);
        printf("%d\n", ans);  
    }  
    return 0;  
} 
View Code

 

posted @ 2016-07-31 14:31  karles~  阅读(415)  评论(0编辑  收藏  举报