标签:statement targe ESS ref chess oar ota unity algo
题目链接:TinyChessboardNim
题目大意:给定一个 \(2\times 2\) 的矩形 \(\begin{bmatrix} a & b \\ c & d \end{bmatrix}\),你每一次可以选择其中的一行或者一列,在选定一个正整数 \(x\),使得这你选择的两个数同时减去 \(x\),需要保证剩下的所有数非负,先后手轮流操作,问能够使得先手必胜的第一步操作有多少种。
题解:我们将原题中的 \(c,d\) 互换一下,就变成了 \(\begin{bmatrix} a & b \\ d & c \end{bmatrix}\),这样的话就是按照顺时针排列了,然后给出本题的重要性质:假设 \(a\) 为矩阵中的最大值,那么如果 \(b\neq d\) 先手必胜(反过来先手不一定必败)。证明考虑归纳:不妨假设 \(b>d\),那么我们可以同时给 \(a,b\) 减去 \(b-d\) 那么就达到了 \(b=d\) 的情况,若此时即为必败态,那么证明完毕。否则根据结论,后手也需要使得与最大值相邻的两个数相等,然而此时后手只有一种行动方式,就是将 \(a,b\) 各自取走 \(a-c\)(若 \(c\ge b\) 依然是后手必败,因为此时先手一定可以模拟后手的操作),然而此时到达的状态依然是先手可以一步到达的(先手只需要一次性多减一些就可以了),所以先手必胜。
上文提到了在 \(c\ge b\) 的情况下若 \(b=d\) 那么先手必败,接下来考虑 \(c<b\) 的情况,容易发现此时先后手都只剩下一条路可走,所以直接计算步数判断奇偶性即可。
时间复杂度 \(O(1)\)。
#include <vector>
#include <cstdio>
#include <algorithm>
using namespace std;
struct TinyChessboardNim{
int ans;
void rotate(vector<int> &a){
int tmp=a[0];
for(int i=1;i<(int)a.size();i++){
a[i-1]=a[i];
}
a.back()=tmp;
}
int get_max(vector<int> &a){
int ans=a[0];
for(int i=1;i<(int)a.size();i++){
ans=max(ans,a[i]);
}
return ans;
}
bool win(vector<int> a){
while(a[0]!=get_max(a)){
rotate(a);
}
if(a[1]!=a[3]){
return 1;
}
if(a[2]>=a[1]){
return 0;
}
int delta=a[0]-a[2];
return a[1]/delta!=a[2]/delta;
}
void check(vector<int> rice,int x){
if(x>0&&rice[0]>=x&&rice[1]>=x){
vector<int> a=rice;
a[0]-=x;
a[1]-=x;
ans+=!win(a);
}
}
int countWinningMoves(vector<int> rice){
swap(rice[2],rice[3]);
ans=0;
for(int i=0;i<4;i++){
check(rice,rice[1]-rice[3]);
if(rice[1]-rice[3]!=rice[0]-rice[2]){
check(rice,rice[0]-rice[2]);
}
rotate(rice);
}
return ans;
}
};
Topcoder 16634(SRM793 Div.1 C) TinyChessboardNim 题解
标签:statement targe ESS ref chess oar ota unity algo
原文地址:https://www.cnblogs.com/withhope/p/14250330.html