题目链接
https://www.luogu.com.cn/problem/CF1066F
题意
二维地图,起始(0,0),给出若干点,要求遍历每一点,两点距离为曼哈顿距离,点有分层level=MAX(X,Y),遍历下一层的点之前一定要遍历全上一层的点,求最短距离。
思路
CF分区是个2100分DP,本来不打算补了(丢给队友.JPG)但见到一篇最短路的题解,看完直呼神仙做法,索性补了一下。
首先需要发现一个特性,我们对于每一层的点,要想保证这一层走的最优,那么这一层遍历的起点和终点都应该是这一层点的"端点"(一层的点可以看作是一个180°旋转的L,我们把这个倒L横竖两条班的顶点称为端点)。因为如果是在不是端点的平凡点上开始/结束,那么在这一层遍历时,一定会出现在这个倒L上重复行走的情况。
那么我们发现了这些特殊点,然后呢?我们用最短路有以下几点需要处理
- 如何保证分层条件?即我们如何建图才能保证一层遍历完才能到下一层?
第一条比较容易解决,我们只需要将点分层,只在上下层间连边,将层级间便利花费隐含在边权之中,不跑坐标点之间的距离最短路,而是跑层级移动最短路即可。
但随之而来的是第二个问题
- 如何将一层内部的遍历花费化成图上的边权?
刚才已经说了,遍历时一定是在端点开始结束的,那么我们在层级间跃迁时其实只有四种情况,下面试加分析
我们设上一层点A,B(A靠左上,B靠右下,后面CD同理),下一层点C,D
我们从AB分别向CD连边,A-C连边代表上一层左上开始到下一层左上开始,那么权即应当为dis(A,B)+dis(B,C),意即上一层左上向右下遍历,在从上一层右下向上一层左上移动的花费,那么对应的A-D,B-C,B-D连边也类似。
这样一来,我们就基本完成了建图,我们将0,0设为起点,向第一层连边,权为距离,最后一层向终点连边,权为最后一层便利花费(两端点距离),再跑最短路即可。
大致思路说完了,那位大佬没有说具体代码实现细节,可能是觉得有手就行,但是我敲了好久,所以也具体说一说,,
用一个装有两个pair的pair数组来维护层级端点,用map映射层级和pair数组里对应的下标。对于输入,如果这个层级第一次出现,那么直接插入到pair后面。如果不是的话,比较一下是否可以作为端点,具体逻辑有点麻烦,有点类似于维护最大值和次大值那样,需要两次比较,可以具体看我的代码。
之后连边部分,s-第一层和最后一层-t需要单独拉出来连边,中间层的连边遍历map一层层的连即可,map是默认对key进行升序排序的,正好契合我们的level属性。
代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
#define int long long
//#define double long double
using namespace std;
typedef long long ll;
const int maxn=800505;
const int inf=0x3f3f3f3f3f3f3f3f;
int n,m,k;
map<int,int>mp;
pair<pair<int,int>,pair<int,int> >pp[maxn];
int p_cnt=1;
int head[maxn];
struct Edge{
int to;
int next;
int w;
} edge[maxn];
int cnt;
int dis[maxn];
void init(){
cnt=0;
memset(head,-1,sizeof(head));
return ;
}
inline void add(int u,int v,int w){
//cout<<u<< ' '<<v<<' '<<w<<endl;
edge[cnt].next=head[u];
edge[cnt].to=v;
edge[cnt].w=w;
head[u]=cnt;
cnt++;
}
typedef pair<int,int> P;
void dij(int start){
memset(dis,0x3f,sizeof(dis));
priority_queue<P,vector<P>,greater<P> > q;
dis[start]=0;
q.push(P(0,start));
while(!q.empty()){
P p=q.top(); q.pop();
int u=p.second;
if(dis[u]<p.first) continue;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(dis[v]>dis[u]+edge[i].w){
dis[v]=dis[u]+edge[i].w;
q.push(P(dis[v],v));
}
}
}
return ;
}
inline int getdis(int x1,int y1,int x2,int y2){
return abs(x1-x2)+abs(y1-y2);
}
signed main(){
IOS
#ifndef ONLINE_JUDGE
freopen("IO\\in.txt","r",stdin);
freopen("IO\\out.txt","w",stdout);
#endif
init();
cin>>n;
for(int i=1;i<=n;i++){
int x,y;
cin>>x>>y;
int rk=max(x,y);
int no=mp[rk];
if(no){
int x1=pp[no].first.first,y1=pp[no].first.second;
int x2=pp[no].second.first,y2=pp[no].second.second;
if(x==rk){//x>=y,右下部分
if(y<=y2){
pp[no].second={x,y};
if(x1==rk)
pp[no].first.second=max(pp[no].first.second,y);
}
else if(x1==rk)
pp[no].first.second=max(pp[no].first.second,y);
}
else{
if(x<=x1){
pp[no].first={x,y};
if(y2==rk)
pp[no].second.first=max(pp[no].second.first,x);
}
else if(y2==rk)
pp[no].second.first=max(pp[no].second.first,x);
}
}
else{
mp[rk]=p_cnt;
pp[p_cnt].first={x,y};
pp[p_cnt++].second={x,y};
}
}
int t=maxn-1;
int ptcnt=1;
int aaaa=0;
auto it=mp.begin();
int rk=it->first;
int ptr=it->second;
int last1x=pp[ptr].first.first,last1y=pp[ptr].first.second;
int last2x=pp[ptr].second.first,last2y=pp[ptr].second.second;
//cout<<"add 0,0"<<"to "<<last1x<<' '<<last1y<<' '<<"with value"<<getdis(0,0,last1x,last1y)<<endl;
//cout<<"add 0,0"<<"to "<<last2x<<' '<<last2y<<' '<<"with value"<<getdis(0,0,last1x,last2y)<<endl;
add(0,1,getdis(0,0,last1x,last1y));
add(0,2,getdis(0,0,last2x,last2y));
for(auto i:mp){
if(!aaaa){
aaaa=1;
continue;
}
rk=i.first;
ptr=i.second;
int pt1x=pp[ptr].first.first,pt1y=pp[ptr].first.second;
int pt2x=pp[ptr].second.first,pt2y=pp[ptr].second.second;
//cout<<pt1x<<' '<<pt1y<<endl;
//cout<<pt2x<<' '<<pt2y<<endl;
add(ptcnt,ptcnt+2,getdis(last1x,last1y,last2x,last2y)+getdis(last2x,last2y,pt1x,pt1y));
add(ptcnt,ptcnt+3,getdis(last1x,last1y,last2x,last2y)+getdis(last2x,last2y,pt2x,pt2y));
add(ptcnt+1,ptcnt+2,getdis(last1x,last1y,last2x,last2y)+getdis(last1x,last1y,pt1x,pt1y));
add(ptcnt+1,ptcnt+3,getdis(last1x,last1y,last2x,last2y)+getdis(last1x,last1y,pt2x,pt2y));
ptcnt+=2;
last1x=pt1x,last1y=pt1y;last2x=pt2x,last2y=pt2y;
}
it=mp.end();
it--;
add(ptcnt,t,getdis(last1x,last1y,last2x,last2y));
add(ptcnt+1,t,getdis(last1x,last1y,last2x,last2y));
dij(0);
cout<<dis[t]<<endl;
}