【NOIP 2015】鬥地主
阿新 • • 發佈:2018-08-08
noi char 生活經驗 中間 也說 其他 一次 輸入 UNC
題目描述
牛牛最近迷上了一種叫鬥地主的撲克遊戲。鬥地主是一種使用黑桃、紅心、梅花、方片的 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 來表示;小王的表示方法為 01 ,大王的表示方法為 02 。
輸出格式:
共 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
題解
爆搜+大模擬:
有人會問:如何爆搜?
並不是真的純爆搜,只是爆搜順子,其余的牌貪心去打,註意順序(代碼裏有解釋);
此題也有一些坑,比如說‘2’不能放在順子裏(我被坑了很久,很少鬥地主),這也說明學OI也是需要一些生活經驗的;
code:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cctype> 6 #define ll long long 7 using namespace std; 8 9 int read(){ 10 int X=0,w=0; char ch=0; 11 while(!isdigit(ch)) {w|=ch==‘-‘;ch=getchar();} 12 while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); 13 return w?-X:X; 14 } 15 int t,n,ans,ord,x,sum; 16 int cnt[200],a[100]; 17 //下面函數都是return 以第i張牌為首能打出順的最大長度 18 int danshunzi(int i){ 19 int p=i; 20 while(cnt[p]>=1&&p<=13)p++; 21 if(p-i>=5)return p; 22 return 0; 23 } 24 int shuangshunzi(int i){ 25 int p=i; 26 while(cnt[p]>=2&&p<=13)p++; 27 if(p-i>=3)return p; 28 return 0; 29 } 30 int sanshunzi(int i){ 31 int p=i; 32 while(cnt[p]>=3&&p<=13)p++; 33 if(p-i>=2)return p; 34 return 0; 35 } 36 //打完順子以後貪心打其他牌的最小次數 37 //第2個while和第3個while順序不能換(雖然打出牌的數量一樣) 38 //因為要是單獨打單的話需要用兩次,而打對需要用一次; 39 //所以貪心帶單 40 int calc(){ 41 int tot=0; 42 memset(a,0,sizeof(a)); 43 for(int i=0;i<=13;i++)a[cnt[i]]++;//桶套桶 ^-^ 44 while(a[4] && a[2]>1) a[4]--,a[2]-=2,tot++;// 8 45 while(a[4] && a[1]>1) a[4]--,a[1]-=2,tot++;// 6 46 while(a[4] && a[2]) a[4]--,a[2]--,tot++;// 6 47 while(a[3] && a[2]) a[3]--,a[2]--,tot++;// 5 48 while(a[3] && a[1]) a[3]--,a[1]--,tot++;// 5 49 return tot+a[1]+a[2]+a[3]+a[4]; 50 } 51 52 void dfs(int dep){ 53 if(dep>=ans) return; 54 int k=calc(); 55 ans=min(ans,dep+k); 56 int pos; 57 for(int i=2;i<=13;i++){ 58 if(sanshunzi(i)){ //三順子 6+ 59 pos=sanshunzi(i); 60 for(int v=i+1;v<=pos-1;v++){ //枚舉長度 61 for(int j=i;j<=v;j++) cnt[j]-=3; 62 dfs(dep+1); 63 for(int j=i;j<=v;j++) cnt[j]+=3; 64 } 65 } 66 } 67 for(int i=2;i<=13;i++){ 68 if(shuangshunzi(i)){ //雙順子 6+ 69 pos=shuangshunzi(i); 70 for(int v=i+2;v<=pos-1;v++){ 71 for(int j=i;j<=v;j++) cnt[j]-=2; 72 dfs(dep+1); 73 for(int j=i;j<=v;j++) cnt[j]+=2; 74 } 75 } 76 } 77 for(int i=2;i<=13;i++){ 78 if(danshunzi(i)){ //單順子 5+ 79 pos=danshunzi(i); 80 for(int v=i+4;v<=pos-1;v++){ 81 for(int j=i;j<=v;j++) cnt[j]--; 82 dfs(dep+1); 83 for(int j=i;j<=v;j++) cnt[j]++; 84 } 85 } 86 } 87 } 88 89 int main() 90 { 91 freopen("cards.in","r",stdin); 92 freopen("cards.out","w",stdout); 93 t=read();n=read(); 94 while(t--){ 95 ans=30; 96 memset(cnt,0,sizeof(cnt)); 97 for(int i=1;i<=n;i++){ 98 x=read();ord=read(); 99 if(x==1)x=13; // A 100 else if(x) x--; // 1對應2 2對應3 ....... 101 cnt[x]++; // 這樣存可以解決2不能在順子裏的坑 102 } 103 dfs(0); 104 printf("%d\n",ans); 105 } 106 return 0; 107 }
我覺得我該鬥兩把地主了 ^-^
【NOIP 2015】鬥地主