SGU 139 Help Needed!

9 篇文章 0 订阅
2 篇文章 0 订阅

最近在刷SGU,感觉虽然已经很久没更新题库了,但是题目还是很不错的能激发思考,学到东西,不知道多久才能把第一版刷完啊。

这题是个小时候就玩过的游戏,拼图,相信大部分人都玩过,小时候玩这个游戏的时候就发现小霸王机子上给的拼图总是能拼回去,但是如果把一幅能拼回去的拼图的两个相邻的方块(不是空格)换个位置,就变成了一个不可能复原的局面,当时就坚信这个结论是对的,然后在这题上实验了一下,AC了,很是开心。不过还是有些东西可以总结的,下面是一些思考:

这题的一个简单思路就是模拟,利用那个0方块去尝试复原整个拼图,模拟过程中设计互换坐标什么的,在0方块移动的方向距离的决策上较为细致地讨论应该是可以出来的,当然可以预见,需要耐心细心,而且代码量应该不小(怪我代码能力太弱,写不好,忽略)。当模拟到最后一行的时候就可以判断这幅图是不是可以复原的了,最后一行是13 14 15 0,但是如果是 13 15 14 0 这种,就是不可能的。

有没有比模拟更好点的方法?

我们分析一下:先从小的开始吧,假设你是有一个2*2的拼图第一行是1 2,第二行是 3 0,这是可行的,但是如果是 2 1 ,3 0就显然不可能复原了,(大家可以尝试一下?)可以发现如果棋盘只有2*2,这三个棋子一直在打圈,就是好像在不停转,空白格(0)的作用基本没用,所以三者相对顺序不变,所以一旦像第二种情况一样就死局了,不可能实现2与1的互换位置,有了这个,接下来就简单些了,3*3也好,4*4也好, 对于n*n的棋盘,其实我们利用空白格,可以先把最上一行和最左边一行复原,于是棋盘就少了一层,可以划归为n - 1的情况,于是乎最终都是可以化为2*2直接判断是不是死局的,这也是上一种模拟的思想,但是我们再多想一步,假设现在是个可以复原的局,我们换两个方格(都不是空格),就变成了死局,那再换一次呢,又变回了可以复原的局,(这其实是可以证明的,但我还没想好,太弱了,表达不出来,姑且先放着),于是我们知道,其实只看相邻两个非零方块交换的次数的奇偶性,这样在模拟的时候就会方便很多了,每次不用再局限于一定要通过0这个方块来换了,相邻的非零格有需要的话也可以交换,最后看这种交换的奇偶性。于是从上到下,从左到右模拟变得十分简单好写,也不用讨论决策的细节。例如:对于x方块,我们算出他本来应该在的行数列数,如果目标地点在当前行上方(其实只可能在上方,因为我们是从上到下模拟的,上面排好了,现在的只可能在自己该在的行上或者更下面)这时候看列数在目标的左右,先调整列数,因为这样才不会影响到原来已经调整好的,例如:正在处理数字6,前面的数字都已经在原来位置,如果6在5正下方,如果直接交换上下这两个,会破坏5,所以先把6移到当前行的第2列,这样保证他在向上调整的时候就不会破坏到前面已经调整好的方块了,其他细节就没有了。如果当前处理的数本来就在他应该在的行的话,就直接调整列,肯定不会破坏前面的(但是不要忘记这一部,我写的时候忘记了)。

代码如下:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<algorithm>
#include<stack>
#include<deque>
#include<list>
#include<set>
#include<vector>
#include<iomanip>
#include<cctype>
#include<string>
#include<memory>
#include<map>
#include<sstream>
#pragma warning (disable : 4996)
#define mem(a) memset(a, 0, sizeof(a))
#define sl(a) strlen(a)
#define LL long long
#define dou double
const int Mod = 1000000007;
const int N = 300005;
using namespace std;

struct po{
	int x, y;
}now[20];

void swap(po &a, po &b){
	po tem;
	tem = a; a = b; b = tem;
}

int s[5][5];

int go(void){
	int cnt = 0, i, j, k, t, x, y, tem;
	for (i = 1; i < 16; ++i){
		x = now[i].x, y = now[i].y;
		while (x != (i - 1) / 4){
			while (y != (i - 1) % 4){
				if (y > (i - 1) % 4){
					swap(now[i], now[s[x][y - 1]]);
					if (s[x][y - 1]) cnt++;
					tem = s[x][y];
					s[x][y] = s[x][y - 1];
					s[x][y - 1] = tem;
				}
				else{
					swap(now[i], now[s[x][y + 1]]);
					if (s[x][y + 1]) cnt++;
					tem = s[x][y];
					s[x][y] = s[x][y + 1];
					s[x][y + 1] = tem;
				}
				x = now[i].x, y = now[i].y;
			}
			x = now[i].x, y = now[i].y;
			swap(now[i], now[s[x - 1][y]]);
			if (s[x - 1][y]) cnt++;
			tem = s[x][y];
			s[x][y] = s[x - 1][y];
			s[x- 1][y] = tem;
			x = now[i].x, y = now[i].y;
		}
		x = now[i].x, y = now[i].y;//这里这部不能忘,就是行号本来正确的时候还要调整列号,上一步是行号不对的时候先调整列号
		while (y != (i - 1) % 4){
				if (y > (i - 1) % 4){
						swap(now[i], now[s[x][y - 1]]);
						if (s[x][y - 1]) cnt++;
						tem = s[x][y];
						s[x][y] = s[x][y - 1];
						s[x][y - 1] = tem;
				}
				else{
						swap(now[i], now[s[x][y + 1]]);
						if (s[x][y + 1]) cnt++;
						tem = s[x][y];
						s[x][y] = s[x][y + 1];
						s[x][y + 1] = tem;
				}
				x = now[i].x, y = now[i].y;
		}
	}
	return cnt;
}

int main(){
	/*freopen("in.txt", "r", stdin);
	freopen("out.txt", "w",stdout);*/
	int n, i , j, k, cnt =0, x;
	for (i = 0; i < 4; ++i)
		for (j = 0; j < 4; ++j){
			cin >> x; now[x].x = i, now[x].y = j;
			s[i][j] = x;
		}
	cnt = go();
	if (cnt & 1) cout << "NO" << endl;
	else cout << "YES" << endl;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值