bzoj4152[AMPPZ2014] The Captain
题意
给出平面上n个点坐标,点(x1,y1)和点(x2,y2)之间有权值为\(min(abs(x1-x2),abs(y1-y2))\)的边,求1到n的最短路.
分析
老早就知道有这么个题但是一直不会
也许这就是蒟蒻吧.jpg
首先我们可以在(x1,y1)和(x2,y2)之间连两条边,权值分别为abs(x1-x2),abs(y1-y2),然后跑最短路,答案显然不变.
然后我们发现,对从左向右三个点A(x1,y1),B(x2,y2),C(x3,y3),满足x1<=x2<=x3,我们在AB之间连了权值为abs(x1-x2)的边,BC之间连了权值为abs(x2-x3)的边,就不需要在AC之间连权值为abs(x1-x3)的边了.
于是只需按横坐标排序后相邻的点之间连n-1条双向边,按纵坐标排序后相邻的点之间连n-1条双向边.这个边数就可以跑dijkstra了.
这个题还有个最小生成树版本:AtCoder Regular Contest 076D Built?
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=200005,maxm=1000005;
struct edge{
int to,next,w;
}lst[maxm];int len=1,first[maxn];
void addedge(int a,int b,int w){
lst[len].to=b;lst[len].w=w;lst[len].next=first[a];first[a]=len++;
}
struct node{
int v,d;
node(){}
node(int _v,int _d){v=_v;d=_d;}
bool operator <(const node &B)const{
return d>B.d;
}
};
bool vis[maxn];int dis[maxn];
int n;
void dijkstra(){
for(int i=1;i<=n;++i)dis[i]=0x7f7f7f7f;
dis[1]=0;
priority_queue<node> q;
q.push(node(1,0));
while(!q.empty()){
node tmp=q.top();q.pop();
if(vis[tmp.v])continue;
vis[tmp.v]=true;
for(int pt=first[tmp.v];pt;pt=lst[pt].next){
if(!vis[lst[pt].to]&&dis[lst[pt].to]>tmp.d+lst[pt].w){
dis[lst[pt].to]=tmp.d+lst[pt].w;
q.push(node(lst[pt].to,tmp.d+lst[pt].w));
}
}
}
}
int x[maxn],y[maxn];
int seq[maxn];
bool cmpx(const int &a,const int &b){
return x[a]<x[b];
}
bool cmpy(const int &a,const int &b){
return y[a]<y[b];
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d%d",x+i,y+i);
for(int i=1;i<=n;++i)seq[i]=i;
sort(seq+1,seq+n+1,cmpx);
for(int i=1;i<n;++i){
addedge(seq[i],seq[i+1],x[seq[i+1]]-x[seq[i]]);
addedge(seq[i+1],seq[i],x[seq[i+1]]-x[seq[i]]);
}
sort(seq+1,seq+n+1,cmpy);
for(int i=1;i<n;++i){
addedge(seq[i],seq[i+1],y[seq[i+1]]-y[seq[i]]);
addedge(seq[i+1],seq[i],y[seq[i+1]]-y[seq[i]]);
}
dijkstra();
printf("%d\n",dis[n]);
return 0;
}