Luogu P2668 鬥地主(NOIP2015)

本文作者MiserWeyte

還記得那道我只用特判得了30分的“鬥地主”嗎?

我今天腦抽打算把它改A掉。爲什麼不用這大好時光去幹些更有意義的事

於是我就挖了這個坑。

題解:

題目鏈接:P2668 鬥地主

本題就是一道大搜索。按照順序,先搜順子、雙順子、三順子;再搜三張牌、四張牌。
注意順子的回溯部分,以及有四張同點數牌的時候也可以只出三張。
基本思路除了搜索之外就是貪心,優先將搜出來的多張牌一起出。
我是將雙王分開存儲的,防止三/四帶對的時候將雙王帶上。
最後結束出單牌的時候,記得將對子和雙王一起出。
這段代碼過不了加強版數據,需要將貪心的思想換成dp。
源碼有註釋。

//MiserWeyte is now "mzWyt"
#include <bits/stdc++.h>
using namespace std;
int T, n, num, ul, ans;
int card[20]; // 存儲每種牌張數 

void power_sol(){ // 暴力解決n<=4 
    if(n==2){
        int c1, c2;
        while(T--){
            scanf("%d%d%d%d", &c1, &ul, &c2, &ul);
            if(c1==c2) printf("1\n");
            else printf("2\n");
        }
        return;
    }
    if(n==3){
        int c1, c2, c3;
        while(T--){
            scanf("%d%d%d%d%d%d", &c1, &ul, &c2, &ul, &c3, &ul);
            if(c1==c2 && c2==c3) printf("1\n");
            else if(c1 == c2 && c2 != c3) printf("2\n");
            else if(c1 != c2 && c2 == c3) printf("2\n");
            else if(c1 == c3 && c2 != c3) printf("2\n");
            else printf("3\n");
        }
        return;
    }
    if(n==4){
        int c1, c2, c3, c4;
        while(T--){
            scanf("%d%d%d%d%d%d%d%d", &c1, &ul, &c2, &ul, &c3, &ul, &c4, &ul);
            if(c1==c2&&c2==c3&&c3==c4) printf("1\n");
            else if(c1==c2&&c2==c3&&c3!=c4) printf("1\n");
            else if(c1==c2&&c2!=c3&&c2==c4) printf("1\n");
            else if(c1!=c2&&c2==c3&&c3==c4) printf("1\n");
            else if(c1==c2&&c3==c4) printf("2\n");
            else if(c1==c3&&c2==c4) printf("2\n");
            else if(c1==c4&&c2==c3) printf("2\n");
            else if(c1==c2||c1==c3||c1==c4||c2==c3||c2==c4||c3==c4) printf("3\n");
            else printf("4\n");         
        }
        return;
    }
}

void init(){ 
    memset(card, 0, sizeof(card));  // 初始化清空card數組 
    ans = n; // 初始化ans爲n(不存在比全出單張更劣的方案) 

}

void dbg(){ // 調試,輸出當前所有牌 
    cout << endl;
    for(int i=0; i<=14; i++){
        cout << i << '\t' << card[i] << '\n';
    }
    cout << endl;
}

void dfs(int dep){
    if(dep > ans) return; // 剪枝  
    int l; // 當前順子長度 
    l = 0;
    for(int i=3; i<=14; i++){ // 搜索單順子 
        if(card[i]) l ++;
        else l = 0;
        if(l >= 5){ // 長度達到順子 
            for(int j=i-l+1; j<=i; j++) card[j] --; 
//          dbg();
            dfs(dep + 1);
            for(int j=i-l+1; j<=i; j++) card[j] ++;
        }
    }
    l = 0;
    for(int i=3; i<=14; i++){ // 搜索雙順子 
        if(card[i] >= 2) l ++;
        else l = 0;
        if(l >= 3){ // 長度達到雙順子 
            for(int j=i-l+1; j<=i; j++) card[j] -= 2;
            dfs(dep + 1);
            for(int j=i-l+1; j<=i; j++) card[j] += 2;       
        }
    }
    l = 0;
    for(int i=3; i<=14; i++){ // 搜索三順子 
        if(card[i] >= 3) l ++;
        else l = 0;
        if(l >= 2){ // 長度達到三順子 
            for(int j=i-l+1; j<=i; j++) card[i] -= 3;
            dfs(dep + 1);
            for(int j=i-l+1; j<=i; j++) card[i] += 3;       
        }
    }
    for(int i=2; i<=14; i++){ // 搜三張牌或四張牌 
        if(card[i] >= 3){ //三帶一張或一對 
            card[i] -= 3;
            for(int j=0; j<=14; j++){
                if(!card[j] || j == i) continue;
                if(card[j]){
                    card[j] --;
                    dfs(dep + 1);
                    card[j] ++;
                }
                if(card[j] >= 2){
                    card[j] -= 2;
                    dfs(dep + 1);
                    card[j] += 2;
                }
            }
            card[i] += 3; 
            if(card[i] == 4){ // 四帶兩張或兩對 
                card[i] -= 4;
                for(int j=0; j<14; j++){ //帶兩張 
                    if(!card[j] || j == i) continue;
                    card[j] --;
                    for(int k=0; k<14; k++){
                        if(!card[k] || k == j || k == i) continue;
                        card[k] --;
                        dfs(dep + 1);
                        card[k] ++;
                    }
                    card[j] ++;
                } 
                for(int j=0; j<14; j++){ //帶兩對 
                    if(card[j] < 2 || j == i) continue;
                    card[j] -= 2;
                    for(int k=0; k<14; k++){
                        if(card[k] < 2 || k == j || k == i) continue;
                        card[k] -= 2;
                        dfs(dep + 1);
                        card[k] += 2;
                    }
                    card[j] += 2;
                } 
                card[i] += 4;
            }
        }
    }
    
    for(int i=0; i<=14; i++){ // 把剩餘的牌打出 
        if(card[i]) dep ++; // 一次性打出所有相同點數的牌
        if(card[i] >= 3) return; // 如果有三張牌或四張牌沒出,一定不是最優 
    }
    if(card[0] && card[1]) dep --; // 雙王可以同時打出 
//  if(dep < ans) dbg(); 
    ans = ans < dep ? ans : dep; // ans取最小 
}

void input(){
    for(int i=0; i<n; i++){
        scanf("%d%d", &num, &ul); // 花色並用不到(useless) 
        if(num == 1) num = 14; // 由於A排在K後面,14存儲A 
        if(num == 0 && ul == 2) num = 1; // 雙王分開存儲,防止被算成一對
        card[num] ++;
    }
}
void work(){    
    if(n <= 4){ // 暴力 
        power_sol();
        return;
    }
    while(T--){
        init();
        input();
//      dbg();
        dfs(0);
        printf("%d\n", ans);
    }  
} 
int main(){
    cin >> T >> n;
    work();
    return 0;
} 

更新記錄


記錄一下,從10.31 16:32 開始改這道題。

upd 10.31 18:45:吃完飯瞅了一下之前模擬賽的時候寫炸的搜索,(已經看不懂了

upd 10.31 22:16:這個月內看來是做不完了(笑 各位Happy Halloween

upd 11.1 14:57:下午翹課來機房。聽取WA聲一片。重寫。

upd 11.3 13:19:咕咕咕(搜索思路錯了 重寫

upd 11.3 21:47:我過了!我過了!(作死提交了下增強數據版)(WA+TLE 80pts)(“fxxk”)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章