1138 : Islands Travel(题目分析+dijkstra求最短路)

 

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

There are N islands on a planet whose coordinates are (X1, Y1), (X2, Y2), (X3, Y3) ..., (XN, YN). You starts at the 1st island (X1, Y1) and your destination is the n-th island (XN, YN). Travelling between i-th and j-th islands will cost you min{|Xi-Xj|, |Yi-Yj|} (|a| denotes the absolute value of a. min{a, b} denotes the smaller value between a and b) gold coins. You want to know what is the minimum cost to travel from the 1st island to the n-th island.

输入

Line 1: an integer N.

Line 2~N+1: each line contains two integers Xi and Yi.

For 40% data, N<=1000,0<=Xi,Yi<=100000.

For 100% data, N<=100000,0<=Xi,Yi<=1000000000.

输出

Output the minimum cost.

样例输入

3
2 2
1 7
7 6

样例输出

2

 

 

嗯,寒假补题练习的第一道题…… 用时差不多是一天半。(药丸 题目拿到手先开始分析,题目的意思是,在一个星球上,存在n个岛屿,每个岛屿都可以和其他任意一个岛屿通行,但是花费是min(abs(xi-xj),abs(yi-yj)),即X轴上的差的绝对值(以后用距离称)和Y轴上两点的距离中的较小值。 问从第一个点到最后一个点的最小花费是多少。 开始推理,这道题每个岛屿之间的花费可以作为一个边的权值,那么这道题初看是一个求N阶完全图的单原最短路问题,很明显如果直接求N阶的话时间复杂度会爆炸(每个节点有N-1条边,那么就是N(N-1)/2条边,即10的10次方~10的9次方左右,对于每次需要遍历一个点的所有边的dijkstra算法来说画面美的不忍直视),当时想到这里的时候我就下意识的把图论的想法给否定了,这个是一个思维错误,详情见后 那既然不能用图论解决,就从贪心和dp下手试试,dp的想法被dijkstra给否定了,这个子问题实在是分的太大,在时间上造成了问题,而贪心,也就是说,最短路径上面的任何一个不包括终点的点,其下一个点必然是相连且边最小的点,这个可以通过下图否定:

temp如图可见,虽然B在距离起点较远,但是下一步到终点的花费比A要小

那么,如果按照贪心算法的话,必然是起点-A-B-终点,但是很明显A-B这个过程的花费是多余的,应在起点直接向A。

好了,贪心,dp,图论都否决了,怎么办?看题解吧……

然后看了题解以后就知道了,对于一个点来说,其作为原点的坐标系里,

X轴正负方向距离最近的两个点和Y轴正负方向最近的两个点构成一个集合,那么最短路的点一定存在在这个集合里面,我们证明一下:

    如果这个结论是错的,那么对于一点来说,在任意一个象限中,存在一个不是距离最短的点v0,这个点存在于最短路L{S,v1,v2,v3,v4…E}中,若V1和S是在Y轴上比较近,那么在Y轴上一定存在一个离S更近的点m0(即上文提到的四个点之一),且这个点在Y轴方向上的V1和S之间:

temp

上图红线标注的区域是m0可能存在的区域,可以看到,假如我们在S和v1之间插入一个点,把新的路径称为L’,那么L’与L的大小之比应该是求d(s,v1)与d(s,m0)+d(m0,v1)之差。

即abs(y_s-y_v1)?(abs(y_s-y_m0)+min(abs(y_v1-y_m0),abs(x_v1-x_m0))

显然问题的结果取决于abs(y_v1-y_m0)与abs(x_v1-x_m0)的大小关系

若abs(x_v1-x_m0)大于abs(y_v1-y_m0),那么结果即为m0到s的Y轴距离加上m0到v1的Y轴距离与原来s到y1的距离比较,那显然是相等的。

若abs(x_v1-x_m0)大于abs(y_v1-y_m0)——最极端的情况就是红色梯形的左上角——此时的m0直接导致s到v1的长度为0,此时L’小于L,这也就是说L并不是最短路,也意味着这个一个v1的点是不存在的。

 

那回到之前的话题,我们应该如何想到存在这么四个点——这四个好像从天上掉下来的点——本题中和以往的判断不同,对于两个点之间求得是x,y轴上坐标的最小值,那么应该可以想到,任意一个最小值都会在另外一个轴上带来位置上对于目标点的靠近,至于靠近的幅度是多少,我们是不知道的,所以x轴,y轴上需要找到两个距离最近的点,而那些距离远的点,如果是需要在对应轴上付出代价的话,那么从最近的点过去和从现在的轴过去所花费的代价是相同的。那为什么是4个?因为我们并不知道目标点是在我们现在所处位置的哪个方向,所以需要在四个象限上进行搜索,那么把x轴正向,负向,y轴正向,负向给查一遍,就可以得到所需的所有可能性了。

 

然后就是愉快的用优先队列+dijkstra搞事情(不过做题的时候因为优先队列不能重载<运算符对指针进行运算导致卡了一下去查度娘……得声明结构体以后使用三个参数的构建函数……)就可以过了,这道题目的启发的话……之前的思路不能完全放弃,注明不行的原因,看看能不能和后面想到的思路结合使用,比如我之前想到的图论,如果和贪心放在一起的话,说不定能够想出来?

 

果然还是有点勉强啊……

代码如下:

 

#include <iostream> 
#include <cstdio> 
#include <cstring> 
#include <sstream> 
#include <string> 
#include <algorithm> 
#include <list> 
#include <map> 
#include <vector> 
#include <queue> 
#include <stack> 
#include <cmath> 
#include <cstdlib> 
#include<climits>
#define MAX 100005
#define ll long long
using namespace std;
struct node
{
    int x, y;
    int id;
    bool vst = 0;
    ll path = INT_MAX;
   
};
struct cmp {
    bool operator() (node *a, node *b)
    {
        return a->path > b->path;
    }
};
inline int reabs(node &a, node &b)
{
    return min(abs(a.x - b.x), abs(a.y - b.y));
}
int comp1(node a, node b)
{
    return a.x < b.x;
}
int comp2(node a, node b)
{
    return a.y < b.y;
}
vector<node>sto;
vector<vector<int>>ac;
vector<node>ori;
vector<bool>vst;
void dij()
{
    priority_queue<node*,vector<node*>,cmp>Q;
    ori[0].path = 0;
    Q.push(&ori[0]);
    ori[0].vst = 1;
    while (!Q.empty())
    {
        node*temp = Q.top();
        Q.pop();
        for (int i = 0; i < ac[temp->id].size(); i++)
        {
            node *obj = &ori[ac[temp->id][i]];//取出相连节点
            int dis = reabs(*temp, *obj);
            if (dis + temp->path < obj->path)//小于则更新
                obj->path = dis + temp->path;
            if (obj->vst == 0)//若该节点第一次遇到,加入优先队列
            {
                obj->vst = 1;
                Q.push(obj);
            }
        }
    }

}
int main()
{
    int N;
    cin >> N;
    sto.resize(N);
    ac.resize(N);
    vst.resize(N,0);
    for (int i = 0; i < N; i++)
    {
        scanf("%d %d", &sto[i].x, &sto[i].y);
        sto[i].id = i;
        sto[i].vst = 0;
    }
    ori = sto;
    sort(sto.begin(), sto.end(), comp1);
    for (int i = 1; i < N ; i++)
    {
        ac[sto[i].id].push_back(sto[i - 1].id);
        ac[sto[i - 1].id].push_back(sto[i].id);
    }
    sort(sto.begin(), sto.end(), comp2);
    for (int i = 1; i < N; i++)
    {
        ac[sto[i].id].push_back(sto[i - 1].id);
        ac[sto[i - 1].id].push_back(sto[i].id);
    }
    dij();
    cout<<ori[N-1].path;
    return 0;
   
}

posted @ 2017-01-21 15:40  duskcloudxu  阅读(735)  评论(0编辑  收藏  举报