[Noip 2015] 斗地主

题目描述

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

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

需要注意的是,本题中游戏者每次可以出手的牌型与一般的斗地主相似而略有不同。具体规则如下:

本题数据随机,不支持hack,要hack或强力数据请点击这里

输入输出格式

输入格式:

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

接下来 TTT 组数据,每组数据 nnn 行,每行一个非负整数对 ai,bia_i,b_iai,bi ,表示一张牌,其中 aia_iai 表示牌的数码, bib_ibi 表示牌的花色,中间用空格隔开。特别的,我们用 111 来表示数码 AAA, 111111 表示数码J JJ, 121212 表示数码Q QQ, 131313 表示数码 KKK;黑桃、红心、梅花、方片分别用 1−41-414 来表示;小王的表示方法为 010101 ,大王的表示方法为 020202 。

输出格式:

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

输入输出样例

输入样例#1: 复制
1 8
7 4
8 4
9 1
10 4
11 1
5 1
1 4
1 1
输出样例#1: 复制
3
输入样例#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
输出样例#2: 复制
6

说明

样例1说明

共有111组手牌,包含8张牌:方片777,方片888,黑桃999,方片101010,黑桃JJJ,黑桃555,方片AAA以及黑桃AAA。可以通过打单顺子(方片777,方片888,黑桃999,方片101010,黑桃JJJ),单张牌(黑桃555)以及对子牌(黑桃AAA以及方片AAA)在333次内打光。

对于不同的测试点, 我们约定手牌组数TTT与张数nnn的规模如下:

数据保证:所有的手牌都是随机生成的。

 


 

 

这道题真的让我感觉我的能力距noip还差远了。

哒哒哒哒哒暴搜都不会写。

说实话这题我看了题解,要不贪心的策略都想不出来。

代码能力太差,写多了就调不出来。

先爆搜顺子,根据题解的意思, 三顺子其实没必要出, 因为不如让他三带1或2,那样更优。

然后贪心爆搜,四带二, 四带两对, 四张炸, 三带一,三带一对, 三个单出,每一个情况都搜索一遍。

剩下的一定就是单和对子了。

再加上最优化剪枝过了。

一开始回溯的时候用的栈存所有改变过的,然后再退栈。

不知为何萎了。

于是又参考了题解233.

 


 

 

 

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
using namespace std;
inline int read() {
    int res = 0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48), ch=getchar();
    return res;
}
#define reg register

int T, n;
int hav[17];//how many cards at beginning, 14 means the king
int ans;

inline bool sanshun(int i) { 
    if (hav[i] >= 3 and hav[i%13+1] >= 3) return 1;
    return 0;
}

inline bool shuangshun(int i) {
    if (hav[i] >= 2 and hav[i+1] >= 2 and hav[(i+1)%13+1] >= 2) return 1;
    return 0;    
}

inline bool danshun(int i) {
    if (hav[i] >= 1 and hav[i+1] >= 1 and hav[i+2] >= 1 and hav[i+3] >= 1 and hav[(i+3)%13+1] >= 1) return 1;
    return 0;    
}

inline int four() {
    for (reg int i = 1 ; i <= 14 ; i ++)
        if (hav[i] >= 4) return i;
    return -1;
}

inline int three() {
    for (reg int i = 1 ; i <= 14 ; i ++)
        if (hav[i] >= 3) return i;
    return -1;
}

void dfs(int dep, int lef) {
    if (dep >= ans) return;
    if (!lef) {ans = dep;return ;}
    for (reg int i = 3 ; i <= 12 ; i ++) { //双顺子 
        if (shuangshun(i)) 
        {
            int k = i;
            while(hav[k] >= 2 and k != 2) 
            {
                hav[k] -= 2;
                if (k - i + 1 >= 3) dfs(dep + 1, lef - (k - i + 1));
                k++;
                if (k == 14) k = 1;
            }
            if (k == 1) k = 14; // 13 -> 1
            else if (k == 2) k = 15; // 1 -> 2
            dfs(dep + 1, lef - (k - i));
            if (k == 15) k--, hav[1] += 2;
            for (reg int j = i ; j <= k - 1 ; j ++) hav[j] += 2;
        }
    }
    for (reg int i = 3 ; i <= 10 ; i ++) { //单顺子 
        if (danshun(i))
        {
            int k = i;
            while(hav[k] >= 1 and k != 2) 
            {
                hav[k]--;
                if (k - i + 1 >= 5) dfs(dep + 1, lef - (k - i + 1));
                k++;
                if (k == 14) k = 1;
            }
            if (k == 1) k = 14;
            else if (k == 2) k = 15;
            dfs(dep + 1, lef - (k - i));
            if (k == 15) k--, hav[1] ++;
            for (reg int j = i ; j <= k - 1 ; j ++) hav[j] ++;
        }
    }
    //四带~~~
    int k = four(); 
    if (k != -1) 
    {
        hav[k] -= 4;
        for (reg int i = 1 ; i <= 14 ; i ++)
        {
            if (!hav[i]) continue;
            for (reg int j = 1 ; j <= 14 ; j ++)
            {
                if (i == j or !hav[j]) continue;
                if (hav[i] >= 2 and hav[j] >= 2) //四带两对 
                {
                    hav[i] -= 2, hav[j] -= 2;
                    dfs(dep + 1, lef - 8);
                    hav[i] += 2, hav[j] += 2;
                }
                if (hav[i] and hav[j]) //四带两张 
                {
                    hav[i]--, hav[j]--;
                    dfs(dep + 1, lef - 6);
                    hav[i]++, hav[j]++;
                }
            }
        }
        dfs(dep + 1, lef - 4);//comboon! 
        hav[k] += 4;
    }
    //三带~~~
    k = three();
    if (k != -1) {
        hav[k] -= 3;
        for (reg int i = 1 ; i <= 14 ; i ++) 
        {
            if (hav[i] >= 2)
            {
                hav[i] -= 2;
                dfs(dep + 1, lef - 5);
                hav[i] += 2;
            }
            if (hav[i]) 
            {
                hav[i]--;
                dfs(dep + 1, lef - 4);
                hav[i]++;
            }            
        }
        dfs(dep + 1, lef - 3);//单三 
        hav[k] += 3;
    }
    //现在只剩下对和单了
    int sum = 0;
    for (reg int i = 1 ; i <= 14 ; i ++)
        if (hav[i]) sum ++;
    dfs(dep + sum, 0);
}

int main()
{
    T = read(), n = read();
    while(T--)
    {
        memset(hav, 0, sizeof hav);
        for (reg int i = 1 ; i <= n ; i ++) {
            int x = read(), y = read();
            if (x != 0) hav[x]++;
            else hav[14]++;
        }
        ans = 14;
        dfs(0, n);
        printf("%d\n", ans);
    }
    return 0;
}

 

posted @ 2018-08-27 22:08  zZhBr  阅读(324)  评论(0编辑  收藏  举报