1. 程式人生 > 實用技巧 >10.4考試 傷心小棧 大模擬

10.4考試 傷心小棧 大模擬

題目描述

相信大家都有玩過“傷心小棧”這款遊戲。

現在有 \(4\) 個玩家來進行操作,我們會介紹本題中的遊戲規則和玩家的出牌策略,你的任務是根據初始局面來計算最後的得分情況。

遊戲開始時, \(4\) 名玩家平分一副撲克牌除去雙王后的 \(52\) 張牌,即每名玩家 \(13\) 張牌。

點數從小到大為 \(2,3,4,5,6,7,8,9,10,J,Q,K,A\) , 輸入中會用 \(11,12,13,1\) 來表示 \(J,Q,K,A\)

我們另用 \(1,2,3,4\) 分別表示梅花、方片、黑桃、紅桃四種花色。

這樣可以用一個二元組來表示一張撲克牌。比如 \((2,7)\) 來表示方片 \(7\)

由於本題中手牌的位置直接影響了玩家的出牌,因此玩家會將手牌排序,規則為先比較花色,再比較點數。

\((1,3),(1,1),(3,10),(4,2),(4,6)\) 就是排好序的手牌。

\(4\) 名玩家坐成一圈,一輪出牌定義為從某名玩家開始按順時針順序每人各出一張牌。

第一輪,從持有梅花 \(2\) 的玩家開始出牌,他需要打出梅花 \(2\)

以後每輪,第一個出牌的玩家都會打出手中最左邊的一張牌。

對於某輪並非第一個出牌的玩家,他會考慮本輪中已經打出的、花色與第一個出牌的玩家打出牌相同的、點數最大的牌。若該玩家沒有與之相同花色的牌,他會將手中最右邊的一張牌打出,否則有兩種情況:

  • 若其手中存在與之相同花色且點數更小的牌,他會選擇相同花色、點數更小的牌中點數最大的打出。

  • 若其手中與之花色相同的牌的點數都更大,他會選擇相同花色、點數最大的牌打出。

一輪遊戲完成後,打出了與第一個出牌的玩家打出牌花色相同且點數最大的玩家收走這一輪打出的 \(4\) 張牌,並由他作為下一輪第一個出牌的玩家開始下一輪。所有牌打完後,統計每名玩家收走的牌。

其中每有一張紅桃牌,該玩家獲得 \(1\) 分。收走黑桃 \(Q\) 的玩家得到 \(13\) 分。

需要注意的是,若一名玩家得到了全部的 \(26\) 分,則改為他不得分,其他 \(3\) 名玩家各得 \(26\) 分。

可以發現,本題中的規則與實際規則會有出入。

本題中 \(4\) 名玩家按順時針的編號為 \(1,2,3,4\)

為了方便選手得到部分分,本題中玩家的牌可能是不完整的,即總量不足 \(13\) 張,但仍保證每人手牌數量相同、沒有相同的牌且梅花 \(2\) 在其中一人手中。

輸入格式

第一行一個正整數 \(T\) 表示資料組數。

對於每組資料,第一行一個正整數 \(n\) 表示每人的手牌數,

接下來 \(4n\) 行依次描述編號為 \(1,2,3,4\) 的玩家的手牌,每人用 \(n\) 行每行一個二元組來表示。

注意給出的手牌不一定有序。

輸出格式

對於每組資料輸出一行 \(4\) 個數一次表示編號為 \(1,2,3,4\) 的玩家的得分。

樣例

樣例輸入

1
2
1 2
4 13
1 10
2 11
1 7
1 4
3 1
3 9

樣例輸出

0 1 0 0 

一看題目比較長,要麼是特別毒瘤的題或者是大模擬題。

考試的時候,怒碼 \(190+\) 行,結果 \(debug\) 用了兩個多小時。

一些要注意的點

  • 注意 \(A\) 這張牌是最大的。
  • 對於某輪並非第一個出牌的玩家,他會考慮本輪中已經打出的、花色與第一個出牌的玩家打出牌相同的、點數最大的牌,這個牌的點數是會變的
  • 剩下的就是注意分情況討論的一些細節

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
#define P pair<int,int>
int T,n,st,score[10],c[10];
bool vis[10][20];//表示這張牌有沒有被用過
vector<pair<int,int> > shu[10];//存每個人收的牌數
struct pai
{
	int col; int num;
}a[10][20];
inline int read()
{
	int s = 0,w = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
	return s * w;
}
bool comp(pai a,pai b)
{
	if(a.col == b.col) return a.num < b.num;
	return a.col < b.col;
}
pair<int,int> find_two()//找有梅花二的那個人
{
	for(int i = 1; i <= 4; i++)
	{
		for(int j = 1; j <= n; j++)
		{
			if(a[i][j].col == 1 && a[i][j].num == 2)
			{
				return make_pair(i,j);
			}
		}
	}
}
int find(int now,int col,int num)//出牌
{
	int id = 0;
	int flag = 0;
	for(int i = 1; i <= n; i++)
	{
		if(a[now][i].col != col || vis[now][i]) continue;
		if(a[now][i].num < num)	{flag = 1; break;}//特判一下情況
	}
	if(flag == 1)
	{
		for(int i = 1; i <= n; i++)
		{
			if(a[now][i].col != col || vis[now][i]) continue;
			if(a[now][i].num > num) break;
			else if(a[now][i].num > a[now][id].num) id = i;
		}
	}
	else
	{
		for(int i = 1; i <= n; i++)
		{
			if(a[now][i].col != col || vis[now][i]) continue;
			if(a[now][i].num > a[now][id].num) id = i; 
		}
	}
	if(id == 0)
	{	
		id = n;
		while(vis[now][id]) id--;
	}
	return id;
}
int slove(int n,int st)
{
	int id = 0;
	if(n == 1)
	{
		P k = find_two();
		int col = a[k.first][k.second].col;
		int num = a[k.first][k.second].num;
		c[k.first] = k.second;
		for(int i = 1; i <= 3; i++)
		{
			int x = (k.first + i) % 4;
			if(x == 0) x = 4;
			c[x] = find(x,col,num);
			if(a[x][c[x]].col == col) num = max(num,a[x][c[x]].num); 
		}
		for(int i = 1; i <= 4; i++)//找收牌的人
		{
			if(a[i][c[i]].col == col && a[i][c[i]].num >= a[id][c[id]].num) id = i;
			vis[i][c[i]] = 1;
		}
		for(int i = 1; i <= 4; i++)//收牌
		{
			shu[id].push_back(make_pair(a[i][c[i]].col,a[i][c[i]].num));
		}
		return id;
	}
	else
	{
		int k = 1;
		while(vis[st][k]) k++;
		int col = a[st][k].col;
		int num = a[st][k].num;
		c[st] = k;
		for(int i = 1; i <= 3; i++)
		{
			int x = (st + i) % 4;
			if(x == 0) x = 4;
			c[x] = find(x,col,num);
			if(a[x][c[x]].col == col) num = max(num,a[x][c[x]].num);
		}
		for(int i = 1; i <= 4; i++)//找收牌的人
		{
			if(a[i][c[i]].col == col && a[i][c[i]].num >= a[id][c[id]].num) id = i;
			vis[i][c[i]] = 1;
		}
		for(int i = 1; i <= 4; i++)
		{
			shu[id].push_back(make_pair(a[i][c[i]].col,a[i][c[i]].num));
		}
		return id;
	}
}
int calc(int now)
{
	int res = 0;
//	printf("-------->\n");
	for(int i = 0; i < shu[now].size(); i++)//計算每個人得分
	{
		P kk = shu[now][i];
//		cout<<kk.first<<" "<<kk.second<<endl;
		if(kk.first == 4) res++;
		else if(kk.first == 3 && kk.second == 12) res += 13;
	}
	return res;
}
void work()
{
	n = read();
	for(int i = 1; i <= 4; i++)
	{
		for(int j = 1; j <= n; j++)
		{
			a[i][j].col = read();
			a[i][j].num = read();
			if(a[i][j].num == 1) a[i][j].num = 14;//A的點數是最大的
		}
	}
	for(int i = 1; i <= 4; i++)
	{
		sort(a[i]+1,a[i]+n+1,comp);//先對牌拍一下序
	}
	int step = 0, st = 0;
	while(step != n)//模擬每一輪的情況
	{
		step++;
		st = slove(step,st);
	}
	for(int i = 1; i <= 4; i++)
	{
		score[i] = calc(i);
		if(score[i] == 26)
		{
			for(int j = 1; j <= 4; j++)//得分情況2
			{
				printf("%d ",j != i ? 26 : 0);
			}
			printf("\n");
			return;
		}
	}
	for(int i = 1; i <= 4; i++)
	{
		printf("%d ",score[i]);
	}
	printf("\n");
	return;
}
void clear()
{
	for(int i = 1; i <= 4; i++)//多測資料要清空
	{
		shu[i].clear();
	}
	memset(vis,0,sizeof(vis));
	memset(score,0,sizeof(score));
}
int main()
{
//	freopen("hearts.in","r",stdin);
//	freopen("hearts.out","w",stdout);
	T = read();
	while(T--)
	{
		clear();
		work();
	}
//	fclose(stdin); fclose(stdout);
	return 0;
}