C - Errich-Tac-Toe
题目描述
给定n*n大小的网格,每个格子内图形为.
,O
或X
,O
和X
的总个数为k。
你需要在最多次操作(把
O
变为X
或把X
变为O
)后,使得网格中每一行和每一列都不存在连续三个O
或X
。
数据范围与提示
。
前言
我连C题都不会。
思路
首先来想Easy Version:显然如果每两个斜排(行标加列标相等的为一个斜排)的X
后面出现一个斜排的O
,那么一定可以把行和列上所有可能的连续三个X
都断开。
(骠的官方的图)红色的斜排上的X
全变为O
以断开三连的X
。
我们设 表示所有满足行标加列标的和取模3等于
的
X
的个数,显然 ,所以
,选
X
的个数最小的方案涂O
一定可以满足要求。
然后想想怎么扩展到Hard Version。
显然每三个斜排里面需要有一排全O
来断开X
,还需要有一排全X
来断开O
。
同理,我们设 ,
表示%3为
的斜排全部涂
O
,%3为 的斜排全部涂
X
的情况下的操作次数,同时再设 表示所有满足行标加列标的和取模3等于
的
O
的个数,那么有 ,
,所以
,还是选操作最少的方案。
当然,有不少更复杂的乱搞做法也把正解的构造搞出来了,由于本质相同,故不提。
代码
我就只贴Hard Version的代码好了。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<ctime>
#define ll long long
#define MAXN 305
#define uns unsigned
#define MOD 998244353ll
#define INF 0x3f3f3f3f
#define lowbit(x) ((x)&(-(x)))
using namespace std;
inline ll read(){
ll x=0;bool f=1;char s=getchar();
while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
return f?x:-x;
}
int n,n1[3],n2[3],zt[3];
char s[MAXN][MAXN];
signed main()
{
for(int T=read();T--;){
n=read();
for(int i=0;i<3;i++)n1[i]=n2[i]=zt[i]=0;
for(int i=1;i<=n;i++){
scanf("%s",s[i]+1);
for(int j=1;j<=n;j++){
if(s[i][j]=='O')n1[(i+j)%3]++;
else if(s[i][j]=='X')n2[(i+j)%3]++;
}
}
int u=0,v=1;
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
if(i!=j&&n2[i]+n1[j]<n2[u]+n1[v])u=i,v=j;
zt[u]=1,zt[v]=2;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(s[i][j]=='X'&&zt[(i+j)%3]==1)s[i][j]='O';
else if(s[i][j]=='O'&&zt[(i+j)%3]==2)s[i][j]='X';
putchar(s[i][j]);
}
putchar('\n');
}
}
return 0;
}