P1985 [USACO07OPEN]翻转棋 题解

CSDN同步

原题链接

简要题意:

给定一个 \(01\) 棋盘,每次可以翻转一个“十”字形(即一个格子连同它四方向的相邻格子,出界则不翻)。求在哪些格子上翻转(十字形的中心)可以使得 翻转后全 \(0\) 且 方案字典序最小

首先 \(n,m \leq 15\),本着面向数据范围做题的原理,分析算法。

算法一

枚举翻转哪些格子进行验证。

时间复杂度:\(O(2^{n \times m} \times n \times m)\),难以接受这样的爆炸性复杂度。

算法二

需要我们分析一下题目。

我们肯定无法枚举 \(n\) 行所有的翻转情况,但我们可以枚举 \(1\) 行。

  • 什么?枚举 \(1\) 行?
  • 没错,我们只枚举第 \(1\) 行是否翻转。
  • 那么,其它行的怎么办呢?
  • 其实很简单。你想:在 按照行的顺序枚举 的情况下,如果 \(a_{i-1,j} = 0\),那么 \(a_{i,j}\) 肯定不翻;因为,其它能够改变 \(a_{i-1,j}\) 的格子已经全部确定,再翻成 \(1\) 就翻不回去了。 那同理,如果 \(a_{i-1,j}=1\),那么 \(a_{i,j}\) 肯定翻,因为 其它能够改变 \(a_{i-1,j}\) 的格子已经全部确定,不翻成 \(0\) 也就翻不回去了。

思路基本成型:枚举第一行的翻转状态,以此递推出每一个格子的翻转状态,模拟翻转验证,记录字典序最小即可。

那么,无解是什么情况呢?

你会发现,如果最后无解的,那就肯定所有的 \(1\) 都在最后一行。

因为,前面的都已经被下面的格子重新翻回去了,而最后一行每人会翻它们。

这样子枚举即可。

时间复杂度:\(O(2^m \times n \times m)\),大概 \(7.3 \times 10^6\),可以通过。

实际得分:\(100pts\).

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

const int N=20;

inline int read(){char ch=getchar();int f=1;while(ch<‘0‘ || ch>‘9‘) {if(ch==‘-‘) f=-f; ch=getchar();}
	int x=0;while(ch>=‘0‘ && ch<=‘9‘) x=(x<<3)+(x<<1)+ch-‘0‘,ch=getchar();return x*f;}

int h[N],a[N][N];
int n,m,b[N][N];
int turn[N][N],p[N][N];
int rev[N],ans=INT_MAX;

const int dx[5]={0,0,0,1,-1};
const int dy[5]={0,1,-1,0,0};

inline void _rev_(int x,int y) {
	for(int i=0;i<5;i++) {
		int nx=x+dx[i],ny=y+dy[i];
		if(nx<1 || ny<1 || nx>n || ny>m) continue;
		b[nx][ny]^=1;
	} //模拟翻转过程
}

inline void check() {
	memset(turn,0,sizeof(turn));
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) b[i][j]=a[i][j]; //备份,准备翻转
	for(int i=1;i<=m;i++)
		if(rev[i]) {
			_rev_(1,i); turn[1][i]=1;
		} //先翻第一行
	for(int i=2;i<=n;i++) for(int j=1;j<=m;j++)
		if(b[i-1][j]) turn[i][j]=1,_rev_(i,j); //依次确定后面的行
	for(int i=1;i<=m;i++) if(b[n][i]) return; //最后一行判断
	int s=0;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++) if(turn[i][j]) s++; //记录 1 的个数,便于字典序排序
	if(s<ans) { //较小
		for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++) p[i][j]=turn[i][j];
		ans=s; //更新答案
	}
}

inline void dfs(int x) { //x 表示当前枚举的是 1 行 x 列
	if(x>m) { //验证
		check(); return;
	} for(int i=0;i<=1;i++)
		rev[x]=i,dfs(x+1); //是否翻转当前格
}

int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=read();
	dfs(1); //枚举第一行状态
	if(ans==INT_MAX) puts("IMPOSSIBLE"); //无解
	else
		for(int i=1;i<=n;i++) {
			for(int j=1;j<=m;j++) printf("%d ",p[i][j]);
			puts("");
		} //输出答案
	return 0;
}

原文地址:https://www.cnblogs.com/bifanwen/p/12636687.html

时间: 2024-08-30 15:14:58

P1985 [USACO07OPEN]翻转棋 题解的相关文章

P1985 [USACO07OPEN]翻转棋

题目链接: 翻转棋 题目分析: 先状压/\(dfs\)枚举第一排状态,然后在每个\(1\)下面翻,即确定了第一排就确定了后面的状态 最后验证一下最后一排是不是全0即可 代码: #include<bits/stdc++.h> #define N 50 using namespace std; inline int read() { int cnt = 0, f = 1; char c = getchar(); while (!isdigit(c)) {if (c == '-') f = -f;

[题解] luogu P1985 [USACO07OPEN]翻转棋

题面 今天学搜索,正好水一发以前做的这道毒瘤题 话说这道题做了我一天,别人都是各种优化,不超100行 天真的我硬核刚了220行,全程0优化水过 但其实不用这么长,我有的函数写的有点重复了( 思路: 显然是dfs,一行一行的来 搜到[i, j]时(i > 1),看[i - 1, j]是否为黑,是的话就翻转[i, j], 也就是说搜完当前行就要保证上一行的棋全都翻成了白色 当搜到最后一行时, 既要保证上一行翻成白色,还要保证自己也都翻成白色, 最后还要特判一下最后两个的翻转. 当时年少轻狂,我想着层

luogu P1541 乌龟棋 题解

\(luogu\) P1541 乌龟棋 题解 题目描述 这道题目想状态的时候想多了一维表示当前走了多少步,其实这个完全没有必要,因为根据你的牌的使用就可以知道你当前在哪一个位置. 状态 设\(f[i][j][k][l]\)表示已经用了\(i\)张步数为\(1\)的牌,\(j\)张步数为\(2\)的牌,\(k\)张步数为\(3\)的牌,\(l\)张步数为\(4\)的牌所能取得的最大分数. 转移 因为只有这四种牌,所以只需要枚举到达当前的步数的最后一步用的那张牌,然后对用四种牌的情况取\(\max\

【基础练习】【背包DP】codevs1068 乌龟棋题解

题目来自2010NOIP提高组 题目描述 Description 小明过生日的时候,爸爸送给他一副乌龟棋当作礼物. 乌龟棋的棋盘是一行N个格子,每个格子上一个分数(非负整数).棋盘第1格是唯一 的起点,第N格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点. -- 1 2 3 4 5 --N 乌龟棋中M张爬行卡片,分成4种不同的类型(M张卡片中不一定包含所有4种类型 的卡片,见样例),每种类型的卡片上分别标有1.2.3.4四个数字之一,表示使用这种卡 片后,乌龟棋子将向前爬行相应的格子数.

乌龟棋 题解

问题描述 小明过生日的时候,爸爸送给他一副乌龟棋当作礼物. 乌龟棋的棋盘是一行N个格子,每个格子上一个分数(非负整数).棋盘第1格是唯一 的起点,第N格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点.…… 1 2 3 4 5 ……N 乌龟棋中M张爬行卡片,分成4种不同的类型(M张卡片中不一定包含所有4种类型 的卡片,见样例),每种类型的卡片上分别标有1.2.3.4四个数字之一,表示使用这种卡 片后,乌龟棋子将向前爬行相应的格子数.游戏中,玩家每次需要从所有的爬行卡片中选择 一张之前没有使

【日常学习】【迭代加深搜索+哈希】codevs1004 四子连棋题解

转载请注明出处 [ametake版权所有]http://blog.csdn.net/ametake 题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向上下左右四个方向移动到相邻的空格,这叫行棋一步,黑白双方交替走棋,任意一方可以先走,如果某个时刻使得任意一种颜色的棋子形成四个一线(包括斜线),这样的状态为目标棋局. ● ○ ●   ○ ● ○ ● ● ○ ● ○ ○ ● ○   输入描述 Input

poj 3279 翻转问题 题解

首先翻转2次和不翻是一样的所以其实答案也是1个0 1 矩阵(虽然这句话是废话- -) 因为之前看过类似的题(好像是个灯泡而不是砖块来着不过记不太清了),只记得每一行的修改都要由它后一行的操作来决定(这在某种程度上是一种贪心的算法所以我觉得这题不算搜索题硬说也是一道贪心题),其实也很好理解,每行的操作会变动上一行如果你研究本行的话之前排好的就乱了所以我们研究每行的下一行. 在这个思路的指导上,我们很容易能得到第二行到倒数第二行的操作,而到了最后一行,如果不全为0,那很明显没有别的操作了,即“IMP

搜索专讲

搜索专讲 Tags:搜索 https://www.zybuluo.com/xzyxzy/note/1058215 前言 做一个专题肯定是要花点时间的 但是哇,搜索怎么这么多内容?!WTF?! 好吧慢慢刷,待四五月份左右出pdf或ppt的讲义吧 先把题目放上,大家愿意的和我一起做吧 题目 李老师给了一个包 广搜 [x] ?POJ1426-Find The Multiple https://vjudge.net/problem/POJ-1426 [x] POJ2251-Dungeon Master

BestCoder Round #90

有生以来第一场在COGS以外的地方打的比赛.挂成dog了. 主要是没有经验,加之代码能力过弱.还有最后的瞎hack三次,Too Young Too Simple...... 言归正传. (抄一发题解先) T1 Kblack loves flag 用两个布尔数组分别维护每个行/列是否被插过旗帜,最后枚举每一行.列统计答案即可.空间复杂度O(n+m),时间复杂度O(n+m+k). T2 dingyeye loves stone 设根节点的深度为0,将所有深度为奇数的节点的石子数目xor起来,则先手必