题目来源:中山纪念中学
题目描述:
Alice和Bob在玩一个游戏,游戏是在一个N*N的矩阵上进行的,每个格子上都有
一个正整数。当轮到Alice/Bob时,他/她可以选择最后一列或最后一行,并将其删除,但
必须保证选择的这一行或这一列所有数的和为偶数。如果他/她不能删除最后一行或最后一
列,那么他/她就输了。两人都用最优策略来玩游戏,Alice先手,问Alice是否可以必胜?
输入:
第一行:T,表示数据组数
对于每组数据的第一行:N
接下来N行,每行N个数,描述这个矩阵
输出:
如果Alice必胜输出W,否则输出L
输入样例
2
2
2 4
6 8
3
5 4 2
1 5 9
7 3 8
输出样例:
L
W
数据范围:
100%数据满足
1<=N<=1000
保证每一行或每一列的和不会超过2*10^9
1<=T<=5
30%数据满足
1<=N<=5
50%数据满足
1<=N<=100
70%数据满足
1<=N<=500
思路:
暴力做法:模拟 50分
1、用a数组存原矩阵,用b数组存原矩阵“列“”的前缀和,用c数组存原矩阵“行”的前缀和,方便快速判断某一列或某一行可不可以删除
2、从右下角开始模拟删除,如果当前行或当前列可以删除,累加删除次数,否则return
3、如果删除次数是奇数,则Alice必输,如果是偶数则必赢
50分代码:
#include<bits/stdc++.h>
using namespace std;
long long t,n,a[1001][1001],b[1001][1001],c[1001][1001],pd=1;
void search(int i,int j)
{
if (b[i][j]%2!=0&&c[i][j]%2!=0||i==0||j==0) return;
if (b[i][j]%2==0) pd++,search(i-1,j);
if (c[i][j]%2==0) pd++,search(i,j-1);
}
int main()
{
pd=1;
cin>>t;
for (int k=1;k<=t;k++)
{
cin>>n;
for (int i=0;i<=n;i++)
for (int j=0;j<=n;j++) b[i][j]=0,c[i][j]=0;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
cin>>a[i][j];
b[i][j]=b[i][j-1]+a[i][j];
c[i][j]=c[i-1][j]+a[i][j];
}
search(n,n);
if (pd%2!=0) cout<<"L"<<endl;
else cout<<"W"<<endl;
}
return 0;
}
正解:DP 100分
听说这是博弈论?噢~原来这就是博弈论
f[i][j]表示从右下角为i,j坐标开始删除Alice先手的输赢状况,1为必赢,0为必输,我们只要把对方置为必输状况就可以赢了,判断当前输赢状况分三种情况
1、当f[i][j]左边和上边都是必赢时,无论当前怎么删,到了对方就是必赢,所以我方必输
2、当只有一个是必赢时,判断可否删除必赢的那一行或列,将对手置于必输状况,如果可以删除,那么当前位置就必赢
3、当两个都必输时,任意删除偶数行或偶数列,都可以将对手置于必输状况,如果都删不了,那么就必输
如果还是不太理解的话,可以将f数组画出来,以第二个矩阵为例子:
最后的f[n][n]为1,必赢,所以输出W
AC代码:
#include<bits/stdc++.h>
using namespace std;
int t,n,a[1001][1001],b[1001][1001],c[1001][1001],f[1001][1001];
int main()
{
cin>>t;
for (int k=1;k<=t;k++)
{
cin>>n;
for (int i=0;i<=n;i++)
for (int j=0;j<=n;j++) f[i][j]=0,b[i][j]=0,c[i][j]=0;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
cin>>a[i][j];
b[i][j]=b[i][j-1]+a[i][j];
c[i][j]=c[i-1][j]+a[i][j];//前缀和
}
if (a[1][1]%2==0) f[1][1]=1; else f[1][1]=0; //初始化
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
if (f[i-1][j]==0&&f[i][j-1]==0)
if (b[i][j]%2==0||c[i][j]%2==0) f[i][j]=1;//只要前两个都是零并且可以删,那么这个位置必赢
if (f[i-1][j]==0&&f[i][j-1]==1)
if (b[i][j]%2==0) f[i][j]=1;
if (f[i-1][j]==1&&f[i][j-1]==0)
if (c[i][j]%2==0) f[i][j]=1; //前两个一个0一个1,对应的可以删就必赢
}
if (f[n][n]==1) cout<<"W"<<endl; //最后是1,必赢
else cout<<"L"<<endl;
}
return 0;
}