UPC-自習課

山再高,往上爬,總能登頂;
路再長,走下去,定能到達。

自習課

題目描述

自習課就是划水課。
你和同桌在玩井字棋,你先手。突然老師進來了。
給定一個局面,問它是否有可能下的出來。
若有可能,求出是否有贏家,若有,輸出贏家。
否則,輸出是否平局,或者下一步是誰的回合。

輸入

有多組數據,第一行給出數據組數 T。
每組數據有 3 行,每行 3 個字符。
若字符爲”X”,表示這裏你下過,若字符爲”O”,表示這裏同桌下過。
若字符爲”.”,表示這裏沒有人下過。

輸出

每個數據輸出一行。
若不可能下的出來,輸出Illegal Situation
若你贏了,輸出X wins,若同桌贏了,輸出O wins
若已經下完了,且平局,輸出Draw
若下一步是你的回合,輸出X's turn
若下一步是同桌的回合,輸出O's turn

Sample Input

5
...
XX.
..O
XOX
XXO
OXO
.O.
XO.
.O.
XOX
OX.
X.O
XXX
OOO
...

Sample Output

O's turn
Draw
Illegal Situation
X wins
Illegal Situation

Hint

對於前 60% 的數據,不存在三個”X”或”O”連成一線的情況;
對於 100% 的數據,T≤100。

思路分析
這題考察的就是模擬的能力。打模擬的要訣就是思維縝密+沉住氣
模擬的代碼都會寫,但是一寫就wa,這就巨難受。本來這個我認爲還OK的代碼能過的,就是在比賽場上浮躁了,自己又推翻重寫。
下面就詳細分析一下我的思路過程。

這題不難,就是問你現在的井字棋走到這個地步了,讓你判斷以下幾個複合什麼情況
X先手
0.不可能出現這個情況
1.X該走了
2.O該走了
3.X 已經贏了
4.O 已經贏了
5.平局(沒有勝負,都符合規則,已經X+O=9了)

仔細想想,如果當前沒有可以連成線的方案,你完全可以根據棋子的數量來判斷合法性,如果合法也很容易知道接下來誰走
方案是,先判斷有無可成線的棋子
判斷X的個數和O的個數
X一定不會比O少
X和O的差距不會大於1
如果滿足這個條件OK,那麼這就是沒有成線且合理
如果X=O那麼就說明該X走了
反之O走
如果上面沒有給出結果,就說明有三點成線
如果說數量差的離譜,直接一尋找X和O的值的大小就可以了,如上步的方法。可以簡單判斷是否合法。
如果數量有可能性,且有連成線的,那麼就得看看,你現在的Win是否合法(只有一人勝出)
這裏就很迷人了。
首先相交的一定是同棋子。(可成的線路相交)而且一個棋子最多爲5(X的)
那麼一定是可行的。
那麼你就尋找一下有沒有是平行的可成線。
如果有判不合法
如果沒有呢?是不是一定就是成立了呢?
不是的,想要成立的最後一個條件就是,我這個成立情形有沒有可能出現。我們在前面只判斷了數量差距的不可能性,但是如果我這一步的上一步就不可能實現呢?
比如X有3個O有3個,想X贏的前提是X有2個O有兩個這是不可能的,想O贏的前天是X有3個O有兩個,這也是不能的。所以,必須得滿足這個條件,如果不能則一定不可。
此上已經表達了滿足了所有可以成立的條件。
如果依然不能判對,那麼這就是錯的。

我把生成全部情況的樣例代碼奉上

#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
int cnt = 0;
int x, y;
char save[20][20];
void id(int t) {
	switch (t) {
	case 0:x = 0, y = 0; break;
	case 1:x = 0, y = 1; break;
	case 2:x = 0, y = 2; break;
	case 3:x = 1, y = 0; break;
	case 4:x = 1, y = 1; break;
	case 5:x = 1, y = 2; break;
	case 6:x = 2, y = 0; break;
	case 7:x = 2, y = 1; break;
	case 8:x = 2, y = 2; break;
	}
}
void dfs(int t) {
	if (t == 9) {
		cnt++;
		for (int i = 0; i < 3; i++)cout << save[i] << endl;
		return;
	}
	id(t);
	save[x][y] = '.';
	dfs(t + 1);
	id(t);
	save[x][y] = 'O';
	dfs(t + 1);
	id(t);
	save[x][y] = 'X';
	dfs(t + 1);
}
int main() {
	freopen("C:\\Users\\hp\\Desktop\\find.txt", "w", stdout);//自由更改路徑
	dfs(0);
	cout << cnt << endl;//注意要把cnt的值自己手動放到頂端
	fclose(stdout);
}

大家可以吧自己和我的生成數據進行對比,一點點的進行調試。

好吧,接下來上代碼

AC時間到

#include<algorithm>
#include<iostream>
#include<string.h>
#include <iomanip>
#include<stdio.h>
#include<utility>
#include<vector>
#include<string>
#include<math.h>
#include<cmath>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#pragma warning(disable:4244)
#define PI 3.141592653589793
#pragma GCC optimize(2)
#define accelerate cin.tie(NULL);cout.tie(NULL);ios::sync_with_stdio(false);
#define EPS 1.0e-8
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll ll_inf = 9223372036854775807;
const int int_inf = 2147483647;
const short short_inf = 32767;
const char char_inf = 127;
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
inline ll read() {
	ll c = getchar(), Nig = 1, x = 0;
	while (!isdigit(c) && c != '-')c = getchar();
	if (c == '-')Nig = -1, c = getchar();
	while (isdigit(c))x = ((x << 1) + (x << 3)) + (c ^ '0'), c = getchar();
	return Nig * x;
}
inline void out(ll a) {
	if (a < 0)putchar('-'), a = -a;
	if (a >= 10)out(a / 10);
	putchar(a % 10 + '0');
}
ll phi(ll n)
{
	ll ans = n, mark = n;
	for (ll i = 2; i * i <= mark; i++)
		if (n % i == 0) { ans = ans * (i - 1) / i; while (n % i == 0)n /= i; }
	if (n > 1)ans = ans * (n - 1) / n; return ans;
}
ll qpow(ll x, ll n, ll mod) {
	ll res = 1;
	while (n > 0) {
		if (n & 1)res = (res * x) % mod;
		x = (x * x) % mod;
		n >>= 1;
	}
	return res;
}
#define Floyd for(int k = 1; k <= n; k++)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
#define read read()
int Num_X, Num_O;
char save[5][5];
short num()
{
	Num_X = 0, Num_O = 0;
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
		{
			if (save[i][j] == 'X')Num_X++;
			else if (save[i][j] == 'O')Num_O++;
		}
	if (Num_X < Num_O || Num_X - Num_O>1)return 0;
	if (Num_X + Num_O == 9)return 5;
	if (Num_X == Num_O)return 1;
	else return 2;
}
bool suc()
{
	bool sw;
	for (int i = 0; i < 3; i++)//判斷行方向,和列方向有沒有成三的
	{
		if (save[i][0] != '.' && save[i][0] == save[i][1] && save[i][0] == save[i][2])
			return true;
		if (save[0][i] != '.' && save[0][i] == save[1][i] && save[0][i] == save[2][i])
			return true;
	}
	if (save[1][1] != '.' && save[0][0] == save[1][1] && save[1][1] == save[2][2])
		return 1;
	if (save[1][1] != '.' && save[2][0] == save[1][1] && save[1][1] == save[0][2])
		return 1;
	return 0;
}
int judge()
{
	if (!strcmp(save[0], "XOX") && !strcmp(save[2], "XOX") && !strcmp(save[1], "OXO"))return 1;//差重合
	//先判斷,如果說沒有走到end,即x/y走下一個,且沒有明顯不合法。就返回3/4;
	if (!suc())return num();//沒有成功的
	if (num() == 0)return 0;//數量不合法
	//有成功的,且數量合法的。
	//如果橫成3,可以有列成三,和斜成三,且一定一樣。如果列成3,可以有橫成三,斜成3,且一定一樣。
	short xisr = 0, oisr = 0, xisc = 0, oisc = 0;
	for (int i = 0; i < 3; i++)
	{
		if (save[i][0] != '.' && save[i][0] == save[i][1] && save[i][0] == save[i][2])
			if (save[i][0] == 'X')xisr++;
			else oisr++;

		if (save[0][i] != '.' && save[0][i] == save[1][i] && save[0][i] == save[2][i])
			if (save[0][i] == 'X')xisc++;
			else oisc++;
	}
	if (xisr && oisr || xisc && oisc)return 0;//如果行列有多三
	int x = Num_X, o = Num_O;
	if (save[1][1] != '.' && save[0][0] == save[1][1] && save[1][1] == save[2][2])
	{
		if (save[1][1] == 'X') { if (x - o == 1)return 3; else return 0; }
		else if (o == x)return 4;
	}
	if (save[1][1] != '.' && save[0][2] == save[1][1] && save[1][1] == save[2][0])
	{
		if (save[1][1] == 'X') { if (x - o == 1)return 3; else return 0; }
		else if (o == x)return 4;
	}
	if (xisr || oisr)
	{
		if (xisr) { if (x - o == 1)return 3; else return 0; }
		else if (o == x) return 4;
	}
	if (xisc || oisc)
	{
		if (xisc) { if (x - o == 1)return 3; else return 0; }
		else if (o == x)return 4;
	}
	return 0;
}
int main()
{
	int T = read;
	while (T--)
	{
		for (int i = 0; i < 3; i++)scanf("%s", save[i]);
		int mark = judge();
		switch (mark)
		{
		case 0:puts("Illegal Situation"); break;
		case 1:puts("X's turn"); break;
		case 2:puts("O's turn"); break;
		case 3:puts("X wins"); break;
		case 4:puts("O wins"); break;
		case 5:puts("Draw"); break;
		}
	}
	return 0;
}

如果有疑問敬請留言。
By-輪月

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