CF1450C - Errich-Tac-Toe——构造

C - Errich-Tac-Toe

题目描述

给定n*n大小的网格,每个格子内图形为.OXOX的总个数为k。

你需要在最多\lfloor\frac{k}{3}\rfloor次操作(把O变为X或把X变为O)后,使得网格中每一行和每一列都不存在连续三个OX

数据范围与提示

 1\le n\le 300

前言

我连C题都不会。

思路

首先来想Easy Version:显然如果每两个斜排(行标加列标相等的为一个斜排)的X后面出现一个斜排的O,那么一定可以把行和列上所有可能的连续三个X都断开。

官解的图片

(骠的官方的图)红色的斜排上的X全变为O以断开三连的X

我们设 a_i 表示所有满足行标加列标的和取模3等于 i 的X的个数,显然 a_0+a_1+a_2=k ,所以 \min(a_0,a_1,a_2)\le \lfloor\frac{k}{3}\rfloor ,选X的个数最小的方案涂O一定可以满足要求。

然后想想怎么扩展到Hard Version

显然每三个斜排里面需要有一排全O来断开X,还需要有一排全X来断开O

同理,我们设 c_{ij} , i,j=0/1/2 表示%3为 i 的斜排全部涂O,%3为 j 的斜排全部涂X的情况下的操作次数,同时再设 b_i 表示所有满足行标加列标的和取模3等于 i 的O的个数,那么有 c_{ij}=a_i+b_j , c_{01}+c_{02}+c_{10}+c_{12}+c_{20}+c_{21}=2k ,所以 \min(c_{01},c_{02},c_{10},c_{12},c_{20},c_{21})=\lfloor\frac{2k}{6}\rfloor=\lfloor\frac{k}{3}\rfloor ,还是选操作最少的方案。

当然,有不少更复杂的乱搞做法也把正解的构造搞出来了,由于本质相同,故不提。

代码

我就只贴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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 两个人在一棵树上玩井字棋游戏,其中一个人是X,另一个人是O。游戏一开始是空的,每轮玩家可以在一个空格子里放置他的符号,当出现任意一方形成了一条长度为3的线(包括水平、竖直、对角线),这个人就赢了。 你需要编写一个程序来判断游戏是否结束,如果游戏结束,你需要输出胜利者的符号('X'或'O'),如果游戏没有结束,你需要输出 "Draw"。 输入格式 第一行包含一个整数 n,表示树的节点的个数。 接下来 n-1 行每行描述一条边,包含两个整数 ui 和 vi,表示树上连接了节点 ui 和节点 vi。 输出格式 如果游戏结束,输出胜利者的符号('X'或'O'),否则输出 "Draw"。 数据范围 1 ≤ n ≤ $10^5$ 输入样例1 6 1 2 1 3 2 4 2 5 3 6 输出样例1 Draw 输入样例2 6 1 2 1 3 2 4 2 5 4 6 输出样例2 O 题目分析 树上博弈论,我们可以考虑用 SG 函数来做,但是 SG 函数的计算比较麻烦,需要考虑子树的 SG 值,我们考虑另外一种方式,直接判断胜负。 对于一个节点 u,我们先 DFS 计算节点 u 的子树中 X 和 O 的个数,设 X 数量为 cntx,O 数量为 cnto。那么对于其父亲节点 v,v 节点的胜负状态有以下几种情况: - 如果 u 节点下有一颗子树中有三个 X,或者有三个 O,那么当前局面就结束了,胜利者为这颗子树中的 X 或 O。 - 如果 u 节点下有一颗子树中有两个 X,或者有两个 O,那么 u 和 u 的子树中除了这颗子树外的所有子树都可以随意选择 X 或 O,所以这颗子树中的 X 或 O 不会影响最终结果,当前节点 v 的胜负状态和其它子树相同。 - 如果 u 节点下有一颗子树中有一个 X,或者有一个 O,那么 u 和 u 的子树中除了这颗子树外的所有子树都可以随意选择 X 或 O,所以这颗子树中的 X 或 O 不会影响最终结果,当前节点 v 的胜负状态取决于其它子树的胜负状态。 时间复杂度 $O(n)$ C++ 代码

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值