BZOJ 4152 The Captain - Dijikstra

思路、证明什么的都在代码的注释里。


需要说明的一点是STL堆默认为大顶堆,一定要记住。。。。


#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<queue>
#include<algorithm>
 
using namespace std;
 
const int maxn=200005<<1;
 
typedef pair<int,int> node;
 
struct edge
{
    int to,next;
    long long val;
}e[maxn<<1];
 
struct point
{
    int x,y,id;
}p[maxn];
 
int n,cnt;
int head[maxn];
bool vst[maxn];
long long dist[maxn];
 
void insert(int a,int b,long long val)
{
    e[++cnt].val=val;e[cnt].to=b;e[cnt].next=head[a];head[a]=cnt;
}
bool cmp1(point a,point b)
{
    return a.x<b.x;
}
bool cmp2(point a,point b)
{
    return a.y<b.y;
}
void dijkstra()
{
    priority_queue< node,vector<node>,greater<node> >q;
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    q.push((node){0,1});
    while(!q.empty())
    {
        int u=q.top().second;
        q.pop();
        if(vst[u])continue;
        vst[u]=true;
        for(int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if(dist[v]>dist[u]+e[i].val)
            {
                dist[v]=dist[u]+e[i].val;
                q.push((node){dist[v],v});
            }
        }
    } 
    printf("%lld",dist[n]);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&p[i].x,&p[i].y);
        p[i].id=i;
    }
    //假设他们相邻,则两点A、B之间连边一定是最优解
    //假设他们y方向相邻,x方向上有一中间点C,那么可以确定如果A,B通过x方向得到的答案必不差于经过C
	//假设通过y方向直接连比经过C优,这是可以做到的,然而y方向上A,B两点已连,所以如此连边可以保证答案正确
	//假设A,B y方向上中间还有D点间隔,那么A,B无法连边,现在考虑A,B无法连边对答案的影响
	//假设A,B直接连严格更优,那么 必然是通过x,y其中一个方向实现的,然而可以确定这两个方向绕过C或D必不比直接连边更差
	//由此假设不成立,所以A,B不连边对答案无影响,由此保证了答案的正确性 
    sort(p+1,p+n+1,cmp1);
    for(int i=2;i<=n;i++)
    {
        insert(p[i].id,p[i-1].id,p[i].x-p[i-1].x);
        insert(p[i-1].id,p[i].id,p[i].x-p[i-1].x);
        //因为不知要向那个方向,所以双向连边,至少要用到一条 
    }
    sort(p+1,p+n+1,cmp2);
    for(int i=2;i<=n;i++)
    {
        insert(p[i].id,p[i-1].id,p[i].y-p[i-1].y);
        insert(p[i-1].id,p[i].id,p[i].y-p[i-1].y);
    }
    dijkstra();
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值