【bzoj4152】【The Captain】最短路(堆优化)(浅尝ACM-B)

这里写图片描述
[pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=62834538

Description

给定平面上的n个点,定义(x1,y1)到(x2,y2)的费用为min(|x1-x2|,|y1-y2|),求从1号点走到n号点的最小费用。
Input

第一行包含一个正整数n(2<=n<=200000),表示点数。
接下来n行,每行包含两个整数x[i],yi,依次表示每个点的坐标。
Output

一个整数,即最小费用。
Sample Input

5

2 2

1 1

4 5

7 1

6 7
Sample Output

2

自然是想到最短路,但一看数据范围,妈呀,吓傻了。dijkstra?o(n*n)的复杂度(当时还不知道有堆优化这东西),肯定TLE。用spfa,竟然也TLE了!?

首先是如何建边的问题,如果把所有的点都建边就太大了,空间都爆了。仔细看题,再画图一思考,即发现只需把x轴、y轴上相邻的的点连起来(明显比其他点优)。由此一来,每个点做多就连4条边。

然后就是算法的优化了。这道题卡spfa,明显摆明了要你用优化。dijktra或spfa都可以用堆优化,手打太麻烦,可以用STL里的优先队列(才学到,长知识了)。dijktra打丑了,一直wa,无可奈何只有改打spfa,结果一遍ac,spfa大法好!

坑点还是蛮多的,放在代码里面了

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define ll long long 
using namespace std;

const int N=200000+5;

int n;
int head[N],end[4*N],nxt[4*N],dis[4*N],hh=0;
struct Node{
    int num,x,y;
}a[N];
ll dist[N];bool vis[N],con[N];

void adde(int a,int b,int di){
    hh++;
    end[hh]=b;
    dis[hh]=di;
    nxt[hh]=head[a];
    head[a]=hh;
}
/*struct cmp{
    bool operator()(int a,int b){
        return dist[a]>dist[b];
    }
};*/
bool cmp1(Node a,Node b){
    return a.x<b.x;
}
bool cmp2(Node a,Node b){
    return a.y<b.y;
}
int abs(int a){
    return a<0?-a:a;
}
/*写wa了的dijkstra
ll dijkstra(int st,int en){
    priority_queue<int,vector<int>,cmp> q;
    memset(dist,0x3f,sizeof(dist));
    dist[st]=0;
    vis[st]=1;
    con[st]=1;
    q.push(st);
    while(!q.empty()){
        int u=q.top();q.pop();
        con[u]=1;
        if(u==en) break;
        for(int i=head[u];i;i=nxt[i]){
            int v=end[i],di=dis[i];//printf("%d ",v);
            if(con[v]==1) continue;
            if(dist[u]+(ll)di<dist[v]){
                dist[v]=dist[u]+(ll)di;
                if(!vis[v]){
                    q.push(v);
                    vis[v]=1;
                }
            }
        }
    }
    return dist[en];
}*/
ll spfa(int st,int en){
    memset(dist,0x3f,sizeof(dist));
    memset(vis,0,sizeof(vis));
    priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > > q;//优先队列(堆)
    q.push(make_pair(0,st));
    vis[st]=1;
    dist[st]=0;
    while(!q.empty()){
        int u=q.top().second;
        q.pop();//q是优先队列,后pop可能就不是u了 
        vis[u]=0;//还可能会被更新
        for(int i=head[u];i;i=nxt[i]){
            int v=end[i],di=dis[i];
            if(dist[v]>dist[u]+di){
                dist[v]=dist[u]+di;
                if(!vis[v]){
                    q.push(make_pair(dist[v],v));
                    vis[v]=1;
                }
            }
        }
    }
    return dist[en];
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&a[i].x,&a[i].y);
        a[i].num=i;
    }
    sort(a+1,a+n+1,cmp1);
    for(int i=1;i<=n;i++){
        if(i>1) adde(a[i].num,a[i-1].num,min(a[i].x-a[i-1].x,abs(a[i].y-a[i-1].y)));//建边要注意了,x轴上相邻的点可能y轴上的距离更近
        if(i<n) adde(a[i].num,a[i+1].num,min(a[i+1].x-a[i].x,abs(a[i].y-a[i+1].y)));
    }
    sort(a+1,a+n+1,cmp2);
    for(int i=1;i<=n;i++){
        if(i>1) adde(a[i].num,a[i-1].num,min(a[i].y-a[i-1].y,abs(a[i].x-a[i-1].x)));
        if(i<n) adde(a[i].num,a[i+1].num,min(a[i+1].y-a[i].y,abs(a[i].x-a[i+1].x)));
    }
    printf("%lld",/*dijkstra(1,n)*/spfa(1,n));
    return 0;
}
posted @ 2017-10-31 19:09  LinnBlanc  阅读(162)  评论(0编辑  收藏  举报