首页 > 其他 > 详细

【Usaco2014Open银组】双导航(gpsdual)

时间:2019-07-08 22:32:38      阅读:136      评论:0      收藏:0      [点我收藏+]

题目

【题目描述】
FJ 最近网购了一台小车。但是由于他的草率,在选择加装物品时偶然地点击了两次“Submit” ,结果最后他的小车装了两台GPS 导航系统!更糟的是,这两个系统对于FJ 要走的路线经常做出矛盾的判断。

FJ 居住地区的地图由N 个路口(2<=N <=10,000)和M 条有向边(1<=M<=50,000)组成。第i 条路连接路口Ai(1 <=Ai<=N)和Bi(1 <=Bi<=N)。对于同一对路口,可能有多条路连接它们;并且一条无向边会被表示成两条方向相反,连接路口相同的边。FJ 的房子位于第一路口,他的农场位于路口N。从他的房子出发,经过一系列有向边,是可以到达农场的。

两个GPS 系统基于如上面所描述的相同地图。然而,每条路它们存储的通过所需时间不相同。道路i 根据第一GPS 系统需要Pi 单位时间来通过,根据第二GPS 系统需要Qi 单位时间来通过(每个通过时间均是在1 到100,000 范围内的整数)。

FJ 想要从他的房子自驾车到农场。然而,当FJ 在一条某一个GPS 系统认为不属于最短路径内(从道路起始点X 到农场的最短路径)的道路(从路口X 到路口Y )上行驶时,这个GPS 系统会大声警告(如果FJ 选择的道路两个系统都不认可,那么两个系统都会警告FJ)。

请帮助FJ 找出,如果他恰当地选择他的路线,他能收到的警告最少是多少。

如果当FJ 沿一条道路走的时候两个GPS 都在警告,那么总警告数+2。


【输入】
第一行,整数N 和M。
接下来M 行,第i 行有四个整数Ai,Bi, Pi,Qi 描述第i 条道路。


【输出】
输出单独一行一个整数——FJ 选择从他家到达农场的最佳线路之后,路上收到的最小总警告数。


【样例输入】
5 7
3 4 7 1
1 3 2 20
1 4 17 18
4 5 25 3
1 2 10 1
3 5 4 14
2 4 6 5


【样例输出】
1

【样例解释】

输入详述:这里有5 个路口和7 条有向道路。第一条道路从路口3 到路口4 连接两个路口;第一GPS 认为这条路需要7 单位时间来通过,接着第二GPS 认为它只需1单位时间,诸如此类。

输出详述:如果FJ 按照路线1->2->4->5 走,那么第一GPS 会在1->2 这条路上发出警告(它认为1->3 这条路更好)。然而,对于路线剩下的2->4->5,两个GPS 都快乐地坐着FJ 的小车通过了,因为每个GPS 都以为这是2 到5 的最短路。


题解

这题主要讲了Farmer John把奶牛们打包送到肯德基后赚了一大笔钱,就网购小汽车。不料脑子瓦特了一下,订了两个GPS。店家黑心,没有告诉FJ这一消息,反而给了他两个版本不同的、声音响亮的GPS导航,这两个导航会在FJ不在走它们所认为的最短路时鸣叫,苦苦折磨着可怜的FJ。FJ就决定找一条去农场的最佳路径,使他被折磨的次数最少。

这题一眼扫过去,最短路算法!
没错,这题就是最短路,直觉没有坑害你。

于是一些人就立刻敲代码了,但是,怎么最短路呢?!

First,做这道题之前,你必须明白一件事——所谓的最短路是指到终点的最短路,当FJ从点 i 出发,走的边不是GPS认为的从点 i 出发去终点所要走的边时(即不在点 i 到终点的最短路上)就会发出警告。

所以要初始化从每一个点出发到终点要走那一条边(只会走一条)。

怎么弄呢?其实可以先跑一次SPFA,计算第一个GPS中每一个点到终点的距离(设第 i 个点的距离为\({dis1_{i}}\)),跑一次SPFA,计算第二个GPS中每一个点到终点的距离(设第 i 个点的距离为\({dis2_{i}}\))。根据SPFA算法的思路,可以推出,对于每一个导航,当点 i 和点 j 通过第 k 条边相连,点 j 到终点的距离被点 i 通过第 k 条边更新时,如果后来都没有任何点更新了点 i 和点 j 的最短路,那么\({dis_{i}+lenth_{k}=dis_{j}}\)\(lenth_{k}\)表示第k条边的长度),由于对于每一个导航都是如此,我就用\(dis\)来代替\(dis1\)\(dis2\)了。

如果点 i 离终点的最短距离被更新了,那么\(dis_{i}+lenth_{k}<dis_{j}\),如果点 j的被更新了,那么\(dis_{i}+lenth_{k}>dis_{j}\)。有人就要问了,如果i和j都被更新了怎么办?那样就必须再白一件事:如果点j到终点的最短路径中仍然是要经过点i的话,那么\(dis_{i}+lenth_{k}=dis_{j}\)(想一想,为什么);如果点j到终点的最短路径已经不用经过点i,那么\(dis_{i}+lenth_{k}<dis_{j}\)

所以,再求出最短路径后,我们就可以枚举每一条边。设这条边的起点是u,终点是v,编号是i,当导航1在FJ经过这一条边时会发出警告,就一定会满足\(dis1_{i}+lenth_{k}>dis1_{j}\);当导航2在FJ经过这一条边时会发出警告,就一定会满足\(dis2_{i}+lenth_{k}>dis2_{j}\)

但是,这一切都搞定了,怎么求最少警告次数呢?

最短路!

我们可以视如果FJ经过一条边会被警告k次,那这一条边的权值就为k,这样弄完以后,再从起点出发跑一次SPFA就好了。

提示:由于本题要跑多次SPFA,建议建一个专门的函数来做SPFA。


代码

#include<cstdio>
#include<cstdlib>
using namespace std;
struct edge
{
    int From,Lenth,To;
    void swap()
    {
        int t=From;
        From=To;
        To=t;
    }
}a[50010],b[50010],c[50010];
int n,m,dis[10010],date[51000],start[10010],end[10010];
bool exist[10010];
int cmp(const void *x,const void *y)
{
    edge xx=*(edge*)x,yy=*(edge*)y;
    if(xx.From!=yy.From) return xx.From-yy.From;
    return xx.To-yy.To;
}
void spfa(int st)
{
    int i,j,u,v,head=0,tail=1;
    qsort(a+1,m,sizeof(edge),cmp);
    for(i=1;i<=n;i++) dis[i]=2e9;
    a[m+1].From=0;
    for(i=1;i<=m+1;i++)
    {
        if(a[i].From!=a[i-1].From)
        {
            start[a[i].From]=i;
            end[a[i-1].From]=i-1;
        }
    }
    date[1]=st,dis[st]=0;
    while(head<tail)
    {
        head++;
        if(head==51000) head=1;
        u=date[head];
        exist[u]=0;
        for(i=start[u];i<=end[u];i++)
        {
            v=a[i].To;
            if(dis[u]+a[i].Lenth<dis[v])
            {
                dis[v]=dis[u]+a[i].Lenth;
                if(!exist[v])
                {
                    exist[v]=1;
                    tail++;
                    if(tail==51000) tail=1;
                    date[tail]=v;
                }
            }
        }
    }
}
int main()
{
    freopen("gpsdual.in","r",stdin);
    freopen("gpsdual.out","w",stdout);
    int i,j,k,x,y,lena,lenb,t;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%d%d",&x,&y,&lena,&lenb);
        a[i]=(edge){y,lena,x};
        b[i]=(edge){y,lenb,x};
        c[i]=(edge){y,0,x};
    }
    qsort(c+1,m,sizeof(edge),cmp);
    spfa(n);
    for(i=1;i<=m;i++)//交换 
    {
        if(dis[a[i].From]+a[i].Lenth>dis[a[i].To]) c[i].Lenth++;
        a[i]=b[i];
    }
    spfa(n);
    for(i=1;i<=m;i++)//交换 
    {
        if(dis[a[i].From]+a[i].Lenth>dis[a[i].To]) c[i].Lenth++;
        c[i].swap();
        a[i]=c[i];
    }
    spfa(1);
    printf("%d\n",dis[n]);
    return 0;
}

【Usaco2014Open银组】双导航(gpsdual)

原文:https://www.cnblogs.com/huangzihaoal/p/11154209.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!