由于是不公平组合游戏,所以不能用SG函数。
但我们可以分析然后DP。
考虑怎么切是最优解。
对于H先手,切一刀后,会让(x,y)变成 (k,y)(x-k,y),即让y可以切的地方乘2.
所以每次尽量选择y小的切,且每次尽量选x/2且,(这样y切的时候能让H可切的地方尽量变多)
对于H先手,切一刀后,变成了(x/2,y),(x/2+(x&1),y).
如果这两个局面都是V先手必败,那么H肯定必胜。
但如果只有一个局面V先手必败会是什么情况呢?(即一个局面V先手必胜,一个局面V先手必败)
考虑V先手必胜的局面,那么肯定是(x/2,y)这个局面(因为这个局面,H可切的地方相对少一点)。
那么这个局面V必胜,V可以一直切这个局面。(一旦H去切另外一个局面,即(x/2+(x&1),y),那么,这个局面必然变成2个V必胜的局面:令z=x/2+(x&1) ;
( z/2,y),( z/2+z&1,y);
(因为z/2一定小于等于z-1,所以切的两个局面的X的值一定都小于x/2,且Y都等于y,即相对于刚才V先手必胜的局面,H可切的变少,V可切的不变。则这两个局面一定都是V先手必胜)
所以H只能跟V对切第一个局面(x/2,y)。
但这个局面是V必胜,则:H最后无处可切,只能切第二个局面,还是V必胜。
综上所述:
对于H先手,只有切一刀产生的2个局面都能使V必败,H才能必胜。
V同理。
有了这个结论,就可以用线性DP进行递推了。
dp[i][j][0], i,j这个状态,H/V先手是否必胜
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e5+7;
/*
int head[M],cnt;
void init(){cnt=0,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y){ee[++cnt].nxt=head[x],ee[cnt].to=y,head[x]=cnt;}
*/
int dp[550][550][2];//i,j,局面,H/V先手是否必胜
int vs[550];
void gt_sg()
{
int n=500;
dp[1][1][0]=dp[1][1][1]=0;
for(int i=1;i<=n;i++)//i行
for(int j=1;j<=n;j++)//j列
{
if(i+j==2)continue;
//0
int tp=0;
for(int k=1;k<i;k++)
tp|=!(dp[k][j][0]||dp[i-k][j][0]);//是否有让后手必败的局面
dp[i][j][1]=tp;
//1
tp=0;
for(int k=1;k<j;k++)
tp|=!(dp[i][k][1]||dp[i][j-k][1]);//是否有让后手必败的局面
dp[i][j][0]=tp;
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
gt_sg();
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
int a,b;
char s[111];
cin>>a>>b>>s;
if(s[0]=='H')
{
if(dp[a][b][0]==0)cout<<"Harry cannot win"<<endl;
else cout<<"Harry can win"<<endl;
}
else
{
if(dp[a][b][1]==0)cout<<"Vicky cannot win"<<endl;
else cout<<"Vicky can win"<<endl;
}
}
return 0;
}