题解 P2060 【[HNOI2006]马步距离】

题目链接

Solution [HNOI2006]马步距离

题目大意:给定一张棋盘,按照马的方式走,问从 ((sx,sy)) 走到 ((tx,ty)) 的最短路

贪心


分析:

首先预处理出它们的相对位置关系,即把 ((sx,sy)) 当作原点建立坐标系。

然后因为马走的方式是对称的,所以坐标系可以随便旋转,也就是坐标直接取绝对值

之后发现当 ((tx,ty)) 很远的时候马走路近似于一条直线,启示我们在大范围考虑贪心,走到比较近的时候我们直接暴力即可

注意可以走到负数位置的情况

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int maxn = 16,limit = 10;
struct point{int x,y;}s,t;
struct node{
	point u;
	int h;
	bool operator < (const node &rhs)const{
		return h > rhs.h;
	}
};
template <class T>
struct marr{
	T val[2 * maxn + 10];
	T &operator[](const int pos){return val[pos + maxn];}
};
marr<marr<int>> dis,vis;
int ans,dx[] = {1,2,1,2,-1,-2,-1,-2},dy[] = {2,1,-2,-1,2,1,-2,-1};
inline bool chk(int x,int y){return x >= -maxn && x <= maxn && y >= -maxn && y <= maxn;}
inline void dij(){
	for(int i = -maxn;i <= maxn;i++)
		for(int j = -maxn;j <= maxn;j++)
			dis[i][j] = 0x7fffffff;
	dis[0][0] = 0;
	priority_queue<node> q;
	q.push(node{point{0,0},0});
	while(!q.empty()){
		point now = q.top().u;q.pop();
		if(vis[now.x][now.y])continue;
		vis[now.x][now.y] = 1;
		for(int i = 0;i < 8;i++){
			int nx = now.x + dx[i],ny = now.y + dy[i];
			if(!chk(nx,ny))continue;
			if(dis[now.x][now.y] + 1 < dis[nx][ny]){
				dis[nx][ny] = dis[now.x][now.y] + 1;
				q.push(node{point{nx,ny},dis[nx][ny]});
			}
		}
	}
}
int main(){
	scanf("%d %d %d %d",&s.x,&s.y,&t.x,&t.y);
	t.x -= s.x,t.y -= s.y;
	s.x = s.y = 0;
	t.x = abs(t.x),t.y = abs(t.y);
	while(t.x > limit || t.y > limit){
		if(t.x < 0)t.x = -t.x;
		if(t.y < 0)t.y = -t.y;
		if(t.x < t.y)swap(t.x,t.y);
		t.x -= 2,t.y -= 1;
		ans++;
	}
	dij();
	printf("%d
",dis[t.x][t.y] + ans);
	return 0;
}

原文地址:https://www.cnblogs.com/colazcy/p/13947293.html