6701. 【2020.06.07省選模擬】旅行

題目

給出一個含有若干個不相交矩形的平面直角座標系,以及起點和終點。
從起點到終點的路途中,不能經過矩形,但是可以在矩形邊經過。
求最短的曼哈頓距離。
n2e5n\leq 2e5


正解

比賽時認爲是歐幾里得距離,所以不存在思考……

先說DYP的做法:
對於一個點,找到上下左右第一個碰到的邊界,然後可以形成一個矩形。這個點可以曼哈頓最短距離到達矩形內任意點。
然後每個頂點以及起點終點向周圍連邊,連的是最近碰到的每個邊界的兩個端點。
然後跑最短路。

在說奇妙的正解:
由於這題的矩形都不相交,於是有些很神奇的性質。
以下建議感性理解,不解釋
首先,橫座標和縱座標至少有一個是單調的。
假設橫座標單調,如何判斷?
欽定起點在終點左邊,假如起點在終點上面,從起點開始不停往下走,遇到障礙就往右。
一直走到終點所在的那一列,如果這時候縱座標小於終點,那麼就不單調。

接下來考慮如何計算答案。
假如起點在終點左邊。從起點開始往右掃,遇到障礙就分兩路到障礙的兩邊,並且將橫座標移到障礙這裏。(如果沒有遇到障礙,先暫時保持着橫座標不變,具體原因自己去玩)
一直這麼做下去,直到計算過兩列之間的所有障礙。
剩下的距離就是最短的曼哈頓距離。

無法用精準的語言來說明它的正確性,但它就是很對……
這個東西隨便用set維護就可以了。


代碼

具體細節在此

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <climits>
#include <set>
#define N 200010
int n;
struct DOT{
	int x,y;
	void swap(){std::swap(x,y);}
} S,T;
struct Sqr{
	DOT p,q;
} z[N];
int ans=INT_MAX;
struct Segment{
	int x,l,r;
} seg[N*2];
int m;
bool cmp1(Segment a,Segment b){return a.x>b.x;}
bool cmp0(Segment a,Segment b){return a.x<b.x;}
struct Status{
	int y;
	mutable int x,s;
};
bool operator<(Status a,Status b){return a.y<b.y;}
multiset<Status> s;
bool judge(){
	if (S.x>T.x)
		swap(S,T);
	if (S.y>T.y){
		m=0;
		for (int i=1;i<=n;++i)
			if (S.y>z[i].q.y && z[i].q.y>T.y)
				seg[++m]={z[i].q.y,z[i].p.x,z[i].q.x};
		sort(seg+1,seg+m+1,cmp1);
	}
	else{
		m=0;
		for (int i=1;i<=n;++i)
			if (S.y<z[i].p.y && z[i].p.y<T.y)
				seg[++m]={z[i].p.y,z[i].p.x,z[i].q.x};
		sort(seg+1,seg+m+1,cmp0);
	}
	int x=S.x;
	for (int i=1;i<=m;++i)
		if (seg[i].l<x && x<seg[i].r)
			x=seg[i].r;
	return x<=T.x;
}
void calc(){ 
	if (S.x>T.x)
		swap(S,T);
	s.clear();
	m=0;
	for (int i=1;i<=n;++i)
		if (S.x<z[i].p.x && z[i].p.x<T.x)
			seg[++m]={z[i].p.x,z[i].p.y,z[i].q.y};
	sort(seg+1,seg+m+1,cmp0);
	s.insert({S.y,S.x,0});
	for (int i=1;i<=m;++i){
		int x=seg[i].x,l=seg[i].l,r=seg[i].r;
		set<Status>::iterator p=s.lower_bound({l}),u,v;
		bool appear=0;
		for (auto q=p;p!=s.end() && l<p->y && p->y<r;q=p,++p,s.erase(q)){
			if (appear==0){
				appear=1;
				//s.insert() return pair<...,bool>
				u=s.insert((Status){r,x,p->s+(x-p->x)+abs(p->y-r)})/*.first*/;
				v=s.insert((Status){l,x,p->s+(x-p->x)+abs(p->y-l)})/*.first*/;
			}
			else{
				u->s=min(u->s,p->s+(x-p->x)+abs(p->y-r));
				v->s=min(v->s,p->s+(x-p->x)+abs(p->y-l));
			}
		}
	}
	for (auto p=s.begin();p!=s.end();++p)
		ans=min(ans,p->s+(T.x-p->x)+abs(p->y-T.y));
}
int main(){
//	freopen("in.txt","r",stdin);
	freopen("travel.in","r",stdin);
	freopen("travel.out","w",stdout);
	scanf("%d%d%d%d%d",&n,&S.x,&S.y,&T.x,&T.y);
	for (int i=1;i<=n;++i)
		scanf("%d%d%d%d",&z[i].p.x,&z[i].p.y,&z[i].q.x,&z[i].q.y);
	if (judge())
		calc();
	S.swap(),T.swap();
	for (int i=1;i<=n;++i)
		z[i].p.swap(),z[i].q.swap();
	if (judge())
		calc();
	printf("%d\n",ans);
	return 0;
}


總結

還是不要總想着數據結構……
有些問題性質很優美的……

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章