1. 程式人生 > >2018ACM/ICPC南京站網路賽C-GDY (補題)(暴力模擬)

2018ACM/ICPC南京站網路賽C-GDY (補題)(暴力模擬)

題目連結

題意:十三種牌1、2、...、13,和撲克牌一樣,不過JQK換成了11、12、13,大小是一樣的,即3<4<5<...<13<1<2。已知n個人,m張牌,初始每個人從1到n按順序各抽五張牌,可能出現牌堆已抓完但是最後一個人手中不足五張的情況,然後遊戲開始。遊戲過程中,每名玩家輪流出一張牌,第一個出牌的玩家必須出自己手中最小的一張牌,最開始是1先出牌,如果前一名玩家出了某張牌,那麼後一名玩家只能出“恰好”比它大的一張牌,或者出一張2(如果前一名玩家出的不是2),如果後一名玩家無法做到則跳過,換更後一名玩家接牌...如果某名玩家出牌後沒有其他玩家能出牌,則從該玩家開始每名玩家從牌堆中抓一張牌(如果牌堆已抓完則忽略抓牌這一操作),然後再由該名玩家第一個出牌。當某個玩家的牌出光了且牌堆已抓完的時候該玩家為勝者,其餘玩家將手裡的所有牌的數值加起來作為輸掉的分數。資料保證初始抓牌後每個人手中的牌數不為0。

解法當然是直接暴力模擬,不過這種模擬題一般在細節上會容易踩坑。
我在這裡根據自己的經驗講一些坑點和細節:首先,每個人抓五張牌後雖然保證每個人手中都有牌,但最後一個人可能抓了不到五張牌就抓牌結束了,需要判是否break。
然後,每當有人出牌後都需要他判斷手中剩餘牌的數量和牌堆是否已抓完,如果剩餘數量為0且牌堆已抓完說明遊戲結束。
連續沒人出牌需要用一個變數記錄,如果連續n-1人沒有出牌,這一輪出牌結束,從出牌的那個人開始抓牌。
正如出牌過程中要判斷玩家是否已經出完了牌,抓牌過程中也需要判斷牌堆是否已經抓完。
為了方便,我直接把1和2對映為14、15了,因此最好計算分數的時候需要轉化為1和2。
最後是注意迴圈出牌和抓牌時別寫錯了,其實0到n-1的迴圈好寫很多QAQ,但是我習慣了寫1到n,常常不小心寫錯。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define For(i,a,b) for(int i=a;i<=b;i++)
#define INF 0x3f3f3f3f
const int N = 20010;
int t,n,m,cnt;
int num[205][20],pri[205],minnum;
int a[N];
int main(){
	scanf("%d",&t);
	For(cas,1,t){
		scanf("%d %d",&n,&m);
		For(i,1,m) scanf("%d",&a[i]);
		cnt=1;
		memset(num,0,sizeof(num));
		memset(pri,0,sizeof(pri));
		For(i,1,n){
			For(j,1,5){
				if(cnt>m)break;
				num[i][(a[cnt]<3)?(a[cnt]+13):a[cnt]]++,cnt++;
				pri[i]++;
			}
		}
		minnum=pri[1];
		For(i,2,n) if(pri[i]<minnum) minnum=pri[i];
		int now=1,tmp,tt;
		while(minnum>0){
			for(int j=3;j<=15;j++){
				if(num[now][j]>0){
					tmp=j;num[now][j]--;pri[now]--;break;
				}
			}
			if(pri[now]==0&&cnt>m){
				break;
			}
			tt=0;
			for(int i=now%n+1;;i=i%n+1){
				if(tmp==15) break; 
				if(num[i][tmp+1]>0) num[i][tmp+1]--,tmp++,pri[i]--,now=i,tt=0;
				else if(tmp<15&&num[i][15]>0) num[i][15]--,pri[i]--,tmp=15,now=i,tt=0;
				else{
					tt++;
				}
				if(tt==n-1) break;
				if(pri[i]==0&&cnt>m) break;
			}
			if(cnt<=m){
				for(int i=now;;i=i%n+1){
					num[i][(a[cnt]<3)?(a[cnt]+13):a[cnt]]++,cnt++;pri[i]++;
					if(cnt>m) break;
					if(i==((now-2)%n+n)%n+1) break;
				}
			}
			minnum=pri[1];
			For(i,2,n) if(pri[i]<minnum) minnum=pri[i];
		}
		printf("Case #%d:\n",cas);
		int ans;
		For(i,1,n){
			if(pri[i]==0){
				printf("Winner\n");
			}
			else{
				ans=0;
				For(j,3,15){
					if(num[i][j]>0)
					ans+=(num[i][j]*(j<14?j:(j-13)));
				}
				printf("%d\n",ans);
			}
		}
	}
	return 0;
}