1. 程式人生 > >【P2668】鬥地主【NOIP2015】

【P2668】鬥地主【NOIP2015】

題目描述

牛牛最近迷上了一種叫鬥地主的撲克遊戲。鬥地主是一種使用黑桃、紅心、梅花、方片的A到K加上大小王的共54張牌來進行的撲克牌遊戲。在鬥地主中,牌的大小關係根據牌的數碼錶示如下:3<4<5<6<7<8<9<10<J<Q<K<A<2<小王<大王,而花色並不對牌的大小產生影響。每一局遊戲中,一副手牌由 n 張牌組成。遊戲者每次可以根據規定的牌型進行出牌,首先打光自己的手牌一方取得遊戲的勝利。

現在,牛牛隻想知道,對於自己的若干組手牌,分別最少需要多少次出牌可以將它們打光。請你幫他解決這個問題。

需要注意的是,本題中游戲者每次可以出手的牌型與一般的鬥地主相似而略有不同。具體規則如下:

在這裡插入圖片描述

輸入輸出格式

輸入格式:

第一行包含用空格隔開的2個正整數 T,n ,表示手牌的組數以及每組手牌的張數。

接下來 T 組資料,每組資料 n 行,每行一個非負整數對 ai,bi,表示一張牌,其中ai表示牌的數碼,bi表示牌的花色,中間用空格隔開。特別的,我們用 1 來表示數碼 A, 11 表示數碼 J, 12 表示數碼 Q, 13 表示數碼 K;黑桃、紅心、梅花、方片分別用 1−4 來表示;小王的表示方法為 0 1 ,大王的表示方法為 0 2 。

輸出格式:

共 T 行,每行一個整數,表示打光第 i 組手牌的最少次數。

輸入輸出樣例

輸入樣例#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說明

共有1組手牌,包含8張牌:方片7,方片8,黑桃9,方片10,黑桃J,黑桃5,方片A以及黑桃A。可以通過打單順子(方片7,方片8,黑桃9,方片10,黑桃J),單張牌(黑桃5)以及對子牌(黑桃A以及方片A)在3次內打光。

對於不同的測試點, 我們約定手牌組數T與張數n的規模如下:

在這裡插入圖片描述

資料保證:所有的手牌都是隨機生成的。

思路

弱弱的說一句俺鬥地主玩的超級差QAQ

  • 這道題的核心解法在於搜尋(外加一點點的貪心)

  • 由題目分析可知,花色這東西是沒啥用的,所以直接不管

  • 並且容易發現,出牌的順序對於這道題沒有任何影響

因此,我們應該先把順子打出來(這樣我們可以快速+輕鬆+愉快的打出五張以上的牌),剩下的就用貪心,能多帶牌就多帶牌。 順子的話,直接暴力,先搜三順子,到雙順子,再單順子(貪心),在帶牌的時候從四帶二開始一個個列舉,差不多就是這樣。

注意

  • 統計順子的時候不能將大小鬼2統計進去!
  • 四帶二可以帶一對,兩單張,兩對
  • 注意細節!

Code

//鬥地主(搜尋)
#include<bits/stdc++.h>
using namespace std;
int card[20],n,T,ans;
inline int read(){

    	int num=0;
    	char ch;
    	ch=getchar();
    	while(ch<'0'||ch>'9')ch=getchar();
    	while(ch<='9'&&ch>='0'){
		num=num*10+ch-'0';
		ch=getchar();
	}
	return num;
}
void init(){
	int a,b;
	memset(card,0,sizeof(card));
	for(int i=1;i<=n;i++){
		a=read(),b=read();
		if(a>=3&&a<=13)card[a-2]++;
		if(a==1)card[12]++;
		if(a==2)card[13]++;
		if(a==0)card[14]++;
	}
	ans=n;
}
inline int SP(){
	int cnt[5]={0},temp[18]={0},res=0;
	for(int i=1;i<=14;i++)cnt[card[i]]++;
	for(int i=1;i<=14;i++)temp[i]=card[i];
	if(cnt[4]&&(cnt[3]>2||cnt[2]>2)){//四帶二對 
		for(int i=1;i<=14;i++){
			if(temp[i]==4){
				for(int j=1;j<=14;j++){
					if((j==i)||(temp[j]!=2))continue;
					if(temp[i]!=4)break;
					for(int k=1;k<=14;k++){
						if(k==i||k==j)continue;
						if(temp[k]==2){
							temp[i]-=4,temp[j]-=2,temp[k]-=2;
							res++;
							cnt[4]--;
							break;
						}
					}
				}
			}
		}
	}
	if(cnt[4]&&(cnt[3]||cnt[2]||(cnt[1]))){//四帶二單 
		for(int i=1;i<=14;i++){
			if(temp[i]==4){
				for(int j=1;j<=14;j++){
					if((i==j)||temp[j]!=1)continue;
					if(temp[i]!=4)break;
					for(int k=1;k<=14;k++){
						if((i==k)||(j==k))continue;
						if(temp[k]==1){
							temp[i]-=4,temp[j]--,temp[k]--;
							res++;
							break;
						}
					}
				}
			}
		}
	}
	if(cnt[4]&&(cnt[3]||cnt[2])){//四帶一對 
		for(int i=1;i<=14;i++){
			if(temp[i]==4){
				for(int j=1;j<=14;j++){
					if(j==i)continue;
					if(temp[j]==2){
						temp[i]-=4,temp[j]-=2,res++;
						cnt[4]--;
						break;
					}
				}
			}
		} 
	}
	for(int i=1;i<=14;i++){//三帶二、一
		if(temp[i]>=3){
			for(int j=1;j<=14;j++){
				if(i==j)continue;
				if(temp[j]==2){
					temp[i]-=3,temp[j]-=2,res++;
					break;
				}
				if(temp[j]==1){
					temp[i]-=3,temp[j]--,res++;
					break;
				}
			}
		}
	}
	for(int i=1;i<=14;i++){//散牌 
		if(temp[i]==4)temp[i]-=4,res++;
		else if(temp[i]==3)temp[i]-=3,res++;
		else if(temp[i]==2)temp[i]-=2,res++;
		else if(temp[i]==1)temp[i]--,res++;
	}
	return res;
}
void DFS(int deep){
	if(deep>=ans)return;
	for(int i=1;i<=14;i++){
		if(card[i])break;
		if(i==14){
			ans=deep;
			return;
		}
	}
	int temp=SP();
	if(temp+deep<ans)ans=temp+deep;
	int cnt[5]={0};
	for(int i=1;i<=14;i++)cnt[card[i]]++;
	if(cnt[3]>=2){//三順子 
		for(int i=1;i<=10;i++){
            for(int l=2;l<=12;l++){
                for(int p=i;p<=l+i;p++){
                    if((card[p]<3)||(p>12))break;
                    else{
                        if(p==l+i){
                            for(int q=p-l;q<=p;q++)card[q]-=3;
                            cnt[3]-=l+1; 
							DFS(deep+1); 
							cnt[3]+=l+1;
                            for(int q=p-l;q<=p;q++)card[q]+=3;
                        }
                    }
                }
            }
        }
	}
	if(cnt[2]>=3){//雙順子 
		for(int i=1;i<=10;i++){
            for(int l=2;l<=12;l++){
                for(int p=i;p<=l+i;p++){
                    if((card[p]<2)||(p>12))break;
                    else{
                        if(p==l+i){
                            for(int q=p-l;q<=p;q++)card[q]-=2;
                            cnt[2]-=l+1; 
							DFS(deep+1); 
							cnt[2]+=l+1;
                            for(int q=p-l;q<=p;q++)card[q]+=2;
                        }
                    }
                }
            }
        }
	}
	if(cnt[1]+cnt[2]+cnt[3]>5){//單順子 
		for(int i=1;i<=10;i++){
            for(int l=4;l<=12;l++){
                for(int p=i;p<=i+l;p++){
                    if((!card[p])||(p>12))break;
                    else{
                        if(p==l+i){
                            for(int q=p-l;q<=p;q++)card[q]--;
                            DFS(deep+1);
                            for(int q=p-l;q<=p;q++)card[q]++;
                        }
                    }
                }
            }
        }
	}
}
int main(){
	T=read(),n=read();
	while(T--){
		init();
		DFS(0);
		printf("%d\n",ans);
	}
	return 0;
} 

END

PS 說實話俺第一遍沒做出來這道題QAQ是因為我鬥地主太弱的原因嗎15551