简单题意
给出一个n*m的棋盘,一开始有一个棋子在(1,1),两人交替移动棋子,棋子有王,后,马,车四种,移动只能往右下方的子棋盘移动,移动方式按棋子类型有所不同,问谁胜谁负还是平局。
附:国际象棋规则
思路
简单分析一下就可以知道除了马,剩下的三种情况都是必有胜负的,所以可以求一下三种情况下棋盘的SG表,但是直接在程序中动态的打表明显是不现实的,时间复杂度接受不了,但是在本地把SG表打出来以后,一眼就看出了车和王的规律:
车的后手必胜点是只在对角线上,而王的后手必胜点是在横纵坐标都为偶数的点上
这样就省略了两张表,然后仔细观察王后的SG表,发现这个表中零的位置也是有规律的,如下表所示
当前数字 | 是否访问过 | cnt(增量) | 最终坐标 | 操作 |
---|---|---|---|---|
0 | ✘ | 0 | (0,0) | (0,0+cnt),cnt++ |
1 | ✘ | 1 | (1,2) | (1,1+cnt),cnt++ |
2 | ✔ | 2 | (2,1) | none |
3 | ✘ | 2 | (3,5) | (3,3+cnt),cnt++ |
4 | ✘ | 3 | (4,7) | (4,4+cnt),cnt++ |
5 | ✔ | 4 | (5,3) | none |
6 | ✘ | 4 | (6,10) | (6,6+cnt),cnt++ |
7 | ✔ | 5 | (7,4) | none |
8 | ✘ | 5 | (8,13) | (8,8+cnt),cnt++ |
9 | ✘ | 6 | (9,15) | (9,9+cnt),cnt++ |
规律就是从0开始遍历,增量初始化为0,如果当前数字已经已经访问过了,坐标就是之前形成的坐标倒过来,继续下一个数字,否则就在当前数字的基础上加上增量,形成另一个数,构成坐标,并把增量加一。
按照这个规律可以O(n)
的跑一张SG表中零位置的表出来,然后对输入查表就好了。
很显然,坐标是对称的,所以只用记录一个就行了,我这里记录的是x<y
的情况。到时候处理一下输入就行了。
最后是有点麻烦的马,手动推算一下,发现大部分情况下,必败方都能把局面拖向平局,反而能胜利的局面较少,仔细推一下就发现,先手必胜是在对角线两侧,后手必胜是恰好在对角线上,这里的对角线指的是按马的走法形成的对角线,所以要做一个模3的操作,和王后一样,可以把对称的情况在输入上预处理统一一下,简化一下编码。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3+10;
bool qu[maxn]={0};
int queu[maxn];
int main(){
int cnt = 0;
for(int i = 0 ; i < 1010 ; i ++){
if(qu[i] == false) {
queu[i] = i + cnt;
qu[i+cnt] = true;
cnt ++;
}
}
int T;
scanf("%d", &T);
while(T --){
int ty,n,m;
bool flag = false;
scanf("%d %d %d",&ty, &n,&m);
n--;m--;
if(ty == 1) if(n%2 == 0 && m%2 == 0) flag = true;
if(ty == 2) if(n == m) flag = true;
if(ty == 3){
if(n > m) swap(n, m);
if(n == m && n % 3 == 0) flag = true;
else if(n % 3 == 1 && m - n == 1) flag = false;
else {puts("D");continue;}
}
if(ty == 4){
if(n > m) swap(n,m);
if(queu[n] == m) flag = true;
}
if(flag)puts("G");
else puts("B");
}
return 0;
}