组队井字游戏

Farmer John 有 26 头奶牛,恰好她们名字都以不同的字母开头,所以 Farmer John 用每头奶牛的名字的首字母来指代她——一个 A…Z 之间的字母。

这些奶牛最近沉迷于井字游戏,但是由于她们并不满足只有两头奶牛可以一起玩,她们改编了这个游戏,可以让许多奶牛可以一块儿玩!

就像常规的井字游戏一样,这个游戏是在一块 3×3 的棋盘上进行的,只是与仅用 X 和 O 不同,每个格子用一个 A…Z 之间的字母标记,表示占有这个格子的奶牛名字的首字母。

以下是一个棋盘的例子:

COW
XXO
ABC

这些奶牛会在她们迷惑于如何判断胜负之前就占满这九个格子。

显然,就像常规的井字游戏一样,如果任何一头奶牛占有了一整行、一整列,或是一整条对角线,那么这头奶牛就获胜了。

然而,由于奶牛认为多牛游戏中这并不容易出现,所以她们决定允许奶牛组成两牛一队,如果某一行、一列,或是一条对角线仅包含一队的两头奶牛的字母,并且同时包含了这两头奶牛(不仅仅是一头)的字母,那么这一队就获胜。

请帮助奶牛们判断有多少头奶牛或是两头奶牛组成的队伍可以获胜。

注意棋盘上的同一个格子可能在不同奶牛或队伍的获胜中均被用到。

输入格式

输入包含三行,每行三个 A…Z 之间的字符。

输出格式

输出包含两行。

第一行,输出能够获胜的单独的奶牛的数量。

第二行,输出能够获胜的两头奶牛组成的队伍的数量。

输入样例:

COW
XXO
ABC

输出样例:

0
2

样例解释

在这个例子中,没有单独的奶牛可以获得胜利。

但是,如果奶牛 C 和奶牛 X 组队,她们可以通过 C−X−C 对角线获胜。

同样地,如果奶牛 X 和 O 组队,她们可以通过中间一行取胜。

源代码

#include <iostream>//纯暴力
#include <vector>
#include <algorithm>//排序
using namespace std;
typedef pair<char,char> PII;//数对
const int N = 10;
char g[N][N];//初始化九宫格
vector<char> a;//存第一组的解
vector<char> t;//临时组用来缓存第二组的解
vector<PII> b;//存第二组的解
int main()
{
	for(int i = 1;i <= 3;i ++ )
	{
		for(int j = 1;j <= 3;j ++ )
		{
			cin>>g[i][j];//输入图
		}
	}
	int ans1=0,ans2=0;
	for(int i = 1;i <= 3;i ++ )//逐行扫
	{
		if(g[i][1]==g[i][2]&&g[i][2]==g[i][3])a.push_back(g[i][1]);//因整行都相同,存一个即可
	}
	for(int i = 1;i <= 3;i ++ )//逐列扫
	{
		if(g[1][i]==g[2][i]&&g[2][i]==g[3][i])a.push_back(g[1][i]);//因整列都相同,存一个即可
	}
	if(g[1][1]==g[2][2]&&g[2][2]==g[3][3])a.push_back(g[1][1]);//正对角线
	if(g[1][3]==g[2][2]&&g[2][2]==g[3][1])a.push_back(g[1][3]);//反对角线
	
	//如何对于第二种情况求解呢?我们可以利用取反
	//正所谓第二组解由两个不同的元素组成三个元素
	//逆向思维就是当三个元素都不同或者当三个元素都相同的相反的情况就是第二种解
	//但要注意,在用缓冲数组存储时,排序后有两种情况,例如XYX排序后是XXY,YXY排序后是XYY
	//我们在往缓冲数组里存的时候只能存两个不同的元素
	//根据规律发现无论哪儿种情况,中间的元素一定是一个解,那么与之相邻的不同元素是另一个解
	
	for(int i = 1;i <= 3;i ++ )
	{
		if(!((g[i][1]==g[i][2]&&g[i][2]==g[i][3])||(g[i][1]!=g[i][2]&&g[i][2]!=g[i][3]&&g[i][1]!=g[i][3])))//逐行扫
		{
			t.push_back(g[i][1]);
			t.push_back(g[i][2]);
			t.push_back(g[i][3]);
			sort(t.begin(),t.end());//进行三个元素按照字母序排序
			if(t[0]==t[1])b.push_back({t[1],t[2]});//一二相同存二三
			else if(t[1]==t[2])b.push_back({t[0],t[1]});//二三相同存一二
			while(t.size()>0)t.pop_back();//手动清空缓冲动态数组
		}
	}
	for(int i = 1;i <= 3;i ++ )
	{
		if(!((g[1][i]==g[2][i]&&g[2][i]==g[3][i])||(g[1][i]!=g[2][i]&&g[2][i]!=g[3][i]&&g[1][i]!=g[3][i])))//逐列扫
		{
			t.push_back(g[1][i]);
			t.push_back(g[2][i]);
			t.push_back(g[3][i]);
			sort(t.begin(),t.end());//进行三个元素按照字母序排序
			if(t[0]==t[1])b.push_back({t[1],t[2]});//一二相同存二三
			else if(t[1]==t[2])b.push_back({t[0],t[1]});//二三相同存一二
			while(t.size()>0)t.pop_back();//手动清空缓冲动态数组
		}
	}
	if(!((g[1][1]==g[2][2]&&g[3][3]==g[2][2])||(g[1][1]!=g[2][2]&&g[2][2]!=g[3][3]&&g[3][3]!=g[1][1])))//正对角线
	{
		t.push_back(g[1][1]);
		t.push_back(g[2][2]);
		t.push_back(g[3][3]);
		sort(t.begin(),t.end());
		if(t[0]==t[1])b.push_back({t[1],t[2]});//一二相同存二三
		else if(t[1]==t[2])b.push_back({t[0],t[1]});//二三相同存一二
		while(t.size()>0)t.pop_back();//手动清空缓冲动态数组
	}
	if(!((g[3][1]==g[2][2]&&g[1][3]==g[2][2])||(g[1][3]!=g[2][2]&&g[2][2]!=g[3][1]&&g[1][3]!=g[3][1])))//反对角线
	{
		t.push_back(g[1][3]);
		t.push_back(g[2][2]);
		t.push_back(g[3][1]);
		sort(t.begin(),t.end());
		if(t[0]==t[1])b.push_back({t[1],t[2]});//一二相同存二三
		else if(t[1]==t[2])b.push_back({t[0],t[1]});//二三相同存一二
		while(t.size()>0)t.pop_back();//手动清空缓冲动态数组
	}

    //对于a动态数组中储存的解进行整理
    sort(a.begin(),a.end());//排序后相同元素相邻
	for(int i = 0;i < a.size();i ++ )if(a[i]!=a[i+1])ans1++;//统计不同的元素个数,即解的个数
	//对于b动态数组中储存的解进行整理
	for(int i = 0;i < b.size();i ++ )
	{
	    int flag=0;//不同的标志
		for(int j = i+1;j < b.size();j ++ )//查找其后是否有与之相同的元素
		{
		    if(b[i].first==b[j].first&&b[i].second==b[j].second)//如果查到
		    {
		        flag=1;//相同标志
		        break;//跳出循环
		    }
		}
		if(flag==0)ans2++;//后无相同元素,解加一
		else if(flag==1)continue;//后有相同元素,继续搜
	}
	cout<<ans1<<endl<<ans2;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/couchpotatoshy/article/details/124572416