1. 程式人生 > >【NOIP 2015】鬥地主 貪心+迭代加深搜尋

【NOIP 2015】鬥地主 貪心+迭代加深搜尋

BZOJ
UOJ正常版本
UOJ變態版本
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

題解:
嗯,我的思路是貪心,在極限資料是過不了的,所以在UOJ最後一組資料過不了,如想看完全正確的思路請看別人的狀壓DP的程式碼。
不過既然是貪心和迭代加深那麼優點,當然是程式碼量短理解簡單,沒有高階的演算法。

嗯,思路就是首先計算四帶二 ,三帶二,四帶二,三帶一能有多要對,然後暴力的計算拆出打三順子,之後打四帶二 ,三帶二,四帶二,三帶一能有多少對,然後暴力的計算拆出打雙個順子,之後打四帶二 ,三帶二,四帶二,三帶一能有多少對,最後暴力的拆出打單順子,之後打四帶二 ,三帶二,四帶二,三帶一能有多少對,因為我們把能一次打更多的先打,這個貪心的思路,可能會有一點點的問題,所以會被極限資料卡掉,但是當時比賽很多人都是選的這個思路,因為簡單,而且不容易打錯。因為是隨機的資料,最後就是拼人品諾,還是有人被扣了5分的。。。。
但是這個程式碼正常的都是AC了的。而且跑的超級快。
這裡寫圖片描述


這裡寫圖片描述
程式碼:

#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
using namespace std;

const int MAXN = 18;

int T,N,ans;
int A[MAXN],cnt[MAXN]; //A 是 碼數  CNT 是 各個數量的多少 

inline int read(){
    int x = 0;char ch = getchar();
    while(ch < '0' || '9' < ch){ch = getchar();}
    while('0' <= ch&& ch <='9'){x = x * 10 + ch - '0';ch = getchar();}
    return x;
}

inline int calc(int x[])
{
    memset(cnt,0,sizeof(cnt));

    int res = 0;

    for(int i = 1;i < MAXN;i++) cnt[ A[i] ]++;
    while(cnt[4] && cnt[2] >= 2) res++,cnt[4]--,cnt[2] -= 2;//四帶二 
    while(cnt[3] && cnt[2] >= 1) res++,cnt[3]--,cnt[2] -= 1;//三帶二 
    cnt[ A[0] ]++;
    while(cnt[4] && cnt[1] >= 2) res++,cnt[4]--,cnt[1] -= 2;//四帶二 
    while(cnt[3] && cnt[1] >= 1) res++,cnt[3]--,cnt[1] -= 1;//三帶一
    while(cnt[4] >= 2) res++,cnt[4] -= 2;
    return res + cnt[4] + cnt[3] + cnt[2] + cnt[1];
}

inline void dfs(int x[],int step){
    if(step > ans) return;
    ans = min(ans,step + calc(x));

    for(int i = 2;i < MAXN;i++){    //三順子
        int j; 
        for(j = i;x[j] >= 3;j++);

        if(j - i >= 2){             //湊成三順子 
            for(int t = j;t - i >= 2;t--){
                for(int k = i;k < t;k++) x[k] -= 3;
                dfs(x,step+1);
                for(int k = i;k < t;k++) x[k] += 3;
            }
        }
    }

    for(int i = 2;i < MAXN;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(x,step+1);
                for(int k = i;k < t;k++) x[k] += 2;
            }
        }
    }

    for(int i = 2;i < MAXN;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(x,step+1);
                for(int k = i;k < t;k++) x[k] += 1;
            }
        }
    }
}

int main(){
    T = read();N = read();

    while(T--){
        memset(A,0,sizeof(A));
        for(int i = 1;i <= N;i++){
            int w = read(),c = read();
            if(w == 1) w = 13;
            else if(w) w--;
            A[w]++;
        }
        ans = calc(A);
        dfs(A,0);
        printf("%d\n",ans);
    }

    return 0;
}