2019 GDUT Rating Contest III : Problem C. Team Tic Tac Toe

题面:

C. Team Tic Tac Toe

Input file: standard input
Output file: standard output
Time limit: 1 second
Memory limit: 256 megabytes
 
Farmer John owns 26 cows, which by happenstance all have names starting with different letters of the alphabet, so Farmer John typically refers to each cow using her first initial – a character in the range A…ZA…Z.
The cows have recently become fascinated by the game of tic-tac-toe, but since they don’t like the fact that only two cows can play at a time, they have invented a variant where multiple cows can play at once! Just like with regular tic-tac-toe, the game is played on a 3×3 board, only instead of just Xs and Os, each square is marked with a single character in the range A...Z to indicate the initial of the cow who claims that square.
An example of a gameboard might be:

COW
XXO
ABC

The cows fill in each of the nine squares before they become confused about how to figure out who has won the game. Clearly, just like with regular tic-tac-toe, if any single cow has claimed an entire row, column, or diagonal, that cow could claim victory by herself. However, since the cows think this might not be likely given the larger number of players, they decide to allow cows to form teams of two, where a team of two cows can claim victory if any row, column, or diagonal consists only of characters belonging to the two cows on the team, and moreover if characters from both cows (not just one) are used in this row, column, or diagonal.
Please help the cows figure out how many individuals or two-cow teams can claim victory. Note that the same square on the game board might possibly be usable in several different claims to victory.
 
 
Input
The input consists of three lines, each of which is three characters in the range A...Z.
 
Output
Output should consist of two lines. On the first line, output the number of individual cows who can claim victory. On the second line, output the number of two-cow teams that could claim victory.
 
Example
Input
COW
XXO
ABC
Output
2
 
Note
In this example, no single cow can claim victory. However, if cows C and X team up, they can win via the C-X-C diagonal. Also, if cows X and O team up, they can win via the middle row.
 
 

题目描述:

有26头奶牛,它们的名字分别对应26个字母。最近,奶牛迷上了一款游戏,但是这款游戏只能两头牛同时玩,然后它们发明了一款可以同时多头牛玩的游戏,游戏规则大概是:每头牛都会在一个3 x 3的九宫格里面填上自己的名字,如果有一头牛能占据一整行,或一整列,或者是对角线,那么就说这头牛自称“赢”了。但是,奶牛们考虑到这样只有很少头牛能“赢”,所以,就允许两头奶牛直接组队。如果两头奶牛组队以后能“一起”(这里的“一起”指的是一定要包含两头牛)占据一整行,或一整列,或者对角线,那么,这两头牛组成的队伍“赢”了。现在要求:1.有多少头牛自称“赢”了;2.有多少支由两头牛组成的队伍“赢”了。
 

题目分析:

这道题刚开始题目没有仔细看题目,理解错了。再次看题目时发现有些情况重复计算,改了一下就AC了。
 
下面进入正题:
其实这道题一看这么小,直接暴力就能弄出来了。但是,我的想法也太暴力了,没经过任何思考,代码又长又丑。看了大神的代码才知道原来是这样写的(吐槽博主还没进入正题( ̄_ ̄|||) )。
我先讲讲我的思路(大佬略过,新人慎看,容易被误导):我写了一个检查函数,这个函数可以检测某一个字母是否包含一整行,一整列,还有对角线。然后我从字母A-Z遍历一遍就能求到问题1了,但是呢,问题2怎么求?其实只要从图中任意选两头牛出来就行了,也就是9*9=81的可能,之后用另一个临时二维数组存图,只需要把这两头牛变成同一头牛就可以用检查函数了,然后再把图还原回来,所以又写了一个还原函数。最后用vis数组标记算过的情况,然后就搞定了,AC(恶心)代码如下:
  1 #include <cstdio>
2 #include <cstring>
3 #include <iostream>
4 #include <cmath>
5 #include <set>
6 #include <algorithm>
7 using namespace std;
8 char mymap[5][5];
9 char temp[5][5];
10 int vis[128][128];
11
12 bool check(char ch){
13 int flag;
14 for(int i = 0; i < 3; i++){
15 flag = 1;
16 for(int j = 0; j < 3; j++){
17 if(ch != temp[i][j]) flag = 0;
18 }
19 if(flag) return true;
20 }
21
22 for(int j = 0; j < 3; j++){
23 flag = 1;
24 for(int i = 0; i < 3; i++){
25 if(ch != temp[i][j]) flag = 0;
26 }
27 if(flag) return true;
28 }
29
30 flag = 1;
31 for(int i = 0; i < 3; i++){
32 if(temp[i][i] != ch) flag = 0;
33 }
34 if(flag) return true;
35
36 flag = 1;
37 for(int i = 0; i < 3; i++){
38 if(temp[i][2-i] != ch) flag = 0;
39 }
40 if(flag) return true;
41
42 return false;
43 }
44
45 void reback(){
46 for(int i = 0; i < 3; i++){
47 for(int j = 0; j < 3; j++){
48 temp[i][j] = mymap[i][j];
49 }
50 }
51 }
52
53 void cover(char a, char b){
54 for(int i = 0; i < 3; i++){
55 for(int j = 0; j < 3; j++){
56 if(temp[i][j] == a) temp[i][j] = b;
57 }
58 }
59 }
60
61 void test(){
62 cout <<endl;
63 for(int i = 0; i < 3; i++){
64 for(int j = 0; j < 3; j++){
65 cout << temp[i][j];
66 }
67 cout << endl;
68 }
69
70 }
71
72 int main(){
73 for(int i = 0; i < 3; i++){
74 cin >> mymap[i];
75 }
76
77 reback();
78 //test();
79
80
81 char st[15];
82 int top = 0;
83
84 int cnt1 = 0;
85 for(char i = 'A'; i <= 'Z'; i++){
86 if(check(i)) { cnt1++; st[top++] = i;}
87 }
88 cout << cnt1 << endl;
89
90 for(int i = 0; i < top; i++){
91 int u1 = st[i];
92 for(int j = 0; j < top; j++){
93 int u2 = st[j];
94 vis[u1][u2] = vis[u2][u1] = 1;
95 }
96 }
97
98 int cnt = 0;
99
100
101 for(int ai = 0; ai < 3; ai++){
102 for(int aj = 0; aj < 3; aj++){
103 char ch1 = mymap[ai][aj];
104
105 for(int bi = 0; bi < 3; bi++){
106 for(int bj = 0; bj < 3; bj++){
107 char ch2 = mymap[bi][bj];
108 reback();
109 cover(ch1, ch2);
110
111 //test();
112 if(check(ch2) && !vis[ch1][ch2]) {
113 cnt++;
114 vis[ch1][ch2] = vis[ch2][ch1] = 1;
115 }
116 }
117 }
118 }
119 }
120
121 cout << cnt << endl;
122 return 0;
123 }
但是这个代码巨长(太暴力了),而且很容易出bug(足足花费1小时才写好这个水题),接下来我们讲讲大佬的优秀代码:
首先,我们可以分析一下这道题:要判断一整行,列,对角线是否被同样的牛占据,是不是只要判断一整行,列,对角线的三个元素(字母)是否相等?由此,我们就可以遍历一遍所有行,列,对角线是否被同样的牛占据,这样就可以解决问题1了。问题2呢?其实也是同样的道理:如果只有两头牛占据同一行,列,对角线,那么它们组队后就肯定是“赢”了。只要我们用数组标记这些牛(一维数组),这些  “牛”对(二维数组)被访问过,就可以通过遍历一遍数组来计算出“赢”的个数,不过这里要注意重复的问题。根据上面的想法,我们可以编写一个check函数,检查三个字母是否相等(一头牛“赢”的情况),或者是只有两个字母相等,也就是只有两种字母(对应两头牛组队“赢”的情况),然后在遍历每条边,列,对角线时都执行这个函数就可以完成这道题了。
 
补充:关于重复问题,我们可以这样想:对于一些牛,我怎样才能计算选出两头牛而且这些 "牛"对 不重复的个数,就像这样:
2019 GDUT Rating Contest III : Problem C. Team Tic Tac Toe
显然答案是C(5, 2),但是没学组合数之前,我们是不是这样算:
2019 GDUT Rating Contest III : Problem C. Team Tic Tac Toe
这样就得到了(A,B), (A,C), (A,D), (A,E)
接下来怎样算才不会重复呢?其实也就是这样:
2019 GDUT Rating Contest III : Problem C. Team Tic Tac Toe
这样我们是不是得到了(B,C), (B,D), (B, E)。之后反复执行这个操作就行了
也就是说,当我们遍历 “牛”对 (二维数组)时,通过这种方式就可以检查不同的 “牛”对 之间是否“赢”了,是不是有点神奇(ง •_•)ง,既不会漏也不会重复。怎样写见下面代码:
 
 
AC代码:
 1 #include <cstdio>
2 #include <cstring>
3 #include <iostream>
4 #include <cmath>
5 #include <set>
6 #include <map>
7 #include <algorithm>
8 using namespace std;
9 char g[5][5]; //图
10 int G[5][5]; //转化为整数
11 int win[30], win2[30][30]; //标记数组
12
13 void check(int a, int b, int c){
14 if(a == b && b == c) win[a] = 1; //对应一头牛“赢”的情况
15 else if(a == b && b != c) //下面两个个else if都是其中两头牛组队赢的情况
16 win2[a][c] = win2[c][a] = 1; //双向都要标记一下,去重的关键
17 else if(a == c && b != c)
18 win2[a][b] = win2[b][a] = 1;
19 else if(b == c && a != c)
20 win2[b][a] = win2[a][b] = 1;
21 }
22
23 int main(){
24 for(int i = 0; i < 3; i++) cin >> g[i];
25
26 for(int i = 0; i < 3; i++){
27 for(int j = 0; j < 3; j++){
28 G[i][j] = g[i][j]-'A'; //把字母转化为对应的数字,方便开数组
29 }
30 }
31
32 for(int i = 0; i < 3; i++){
33 check(G[i][0], G[i][1], G[i][2]); //每行
34 check(G[0][i], G[1][i], G[2][i]); //每列
35 }
36
37 check(G[0][0], G[1][1], G[2][2]); //正对角线
38 check(G[0][2], G[1][1], G[2][0]); //负对角线
39
40 int ans = 0;
41 for(int i = 0; i < 26; i++) ans += win[i]; //统计一头牛“赢”的情况
42
43 int ans2 = 0;
44 for(int i = 0; i < 26; i++){ //统计两头牛组队后“赢”的情况
45 for(int j = i+1; j < 26; j++){ //去重关键,只要遍历比i大的牛就行了
46 ans2 += win2[i][j];
47 }
48 }
49
50 cout << ans << endl << ans2 << endl;
51 return 0;
52 }
 
 
 
上一篇:Epic - Tic Tac Toe


下一篇:JavaScript中的数组和字符串