[ABC232H] King‘s Tour 题解(神奇构造题)

题面

一句话题意: 棋盘大小为 h × w h \times w h×w,有一个王停在 ( 1 , 1 ) (1,1) (1,1)。每一步可以走到八联通的格子之一。构造一种方案,使得王经过所有格子恰好一次,并停在 ( a , b ) (a, b) (a,b) 1 ≤ h , w ≤ 100 1 \leq h, w \leq 100 1h,w100

分析: 一道很神奇的构造题, 感觉是一道大分讨,但是题解用特殊的方法可以让程序只有短短 34 34 34 行。

             我们首先考虑当只有两行时该怎么做:
在这里插入图片描述

             因为我们的移动方式非常多样,我们可以通过 下-上-下的方式遍历到 [ 1 , b − 1 ] [1, b-1] [1,b1] 列的所有格子。然后到第 b b b 列我们显然可以直接到达 不是 ( a , b ) (a,b) (a,b) 的那个位置,然后我们只需要跳到 ( 1 , b + 1 ) (1, b + 1) (1,b+1) 后一直沿着第一行走,然后在一直沿着第二行走到 ( 2 , b + 1 ) (2, b + 1) (2,b+1) 后直接跳到 ( a , b ) (a,b) (a,b) 就好了。可以写这么一份代码:

if(h == 2){//只有两行 
		for(int i = 1; i < b; i++) ret.pb(mp(1, i)), ret.pb(mp(2, i));
		ret.pb(mp(3 - a, b));
		for(int i = b + 1; i <= w; i++) ret.pb(mp(1, i));
		for(int i = w; i > b; i--) ret.pb(mp(2, i));
		ret.pb(mp(a, b));
}

              当行数不是 2 2 2 但列数是 2 2 2 时,我们可以把矩形行列交换,相当翻转了一下。然后按照上述方法做就行了。最后记得把路径上的点横纵坐标都交换一下。

if(w == 2){
		vector< PII > res;
		res = solve(w, h, b, a);
		for(auto &x : res) swap(x.first, x.second);
		ret.insert(ret.end(), res.begin(), res.end());
}

             更一般的,我们考虑如果行数也不是 2 2 2,并且列数也不是 2 2 2 该怎么做:我们可以一列一列的消,将大问题转化成小问题。怎么构造方案可以一列一列的消除呢?
在这里插入图片描述

              如果绿色格子里没有终点 ( a , b ) (a, b) (a,b) ,那么我们显然可以依次走绿色的格子,然后相当于是从 L L L 形的右下侧开始一个新的子问题。如果绿色格子里面有终点呢? 最神奇的一步:我们可以把行列交换,这时候终点一定不在交换后的地图的绿色格子里面了。那么问题就得到了解决。

CODE:

#include<bits/stdc++.h>//好强的构造题啊    一直调转矩阵转化成子问题就好了 
#define pb push_back
#define mp make_pair
#define PII pair< int, int >
using namespace std;// 两行的可以搞, 两列的可以看做是反过来 
int h, w, a, b;
vector< PII > res;
vector< PII > solve(int h, int w, int a, int b){//问题:从(1, 1) 出发, 在 h 行 w 列的图上,到 (a, b) 
    vector< PII > ret;
	if(h == 2){//只有两行 
		for(int i = 1; i < b; i++) ret.pb(mp(1, i)), ret.pb(mp(2, i));
		ret.pb(mp(3 - a, b));
		for(int i = b + 1; i <= w; i++) ret.pb(mp(1, i));
		for(int i = w; i > b; i--) ret.pb(mp(2, i));
		ret.pb(mp(a, b));
	}
	else if((w == 2) || (b == 1) || (a == h && b == 2)){//需要调转矩阵 
		vector< PII > res;
		res = solve(w, h, b, a);
		for(auto &x : res) swap(x.first, x.second);
		ret.insert(ret.end(), res.begin(), res.end());
	}
	else{//不需要调转矩阵 
		for(int i = 1; i <= h; i++) ret.pb(mp(i, 1));
		vector< PII > res = solve(h, w - 1, h + 1 - a, b - 1);
		for(auto &x : res) x.first = (h + 1 - x.first), x.second++;
		ret.insert(ret.end(), res.begin(), res.end());
		
	}
	return ret;
}
int main(){
	cin >> h >> w >> a >> b;
	res = solve(h, w, a, b);
	for(auto x : res) cout << x.first << ' ' << x.second << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值