题目

【题目描述】

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)的更多相关文章

  1. BZOJ USACO 银组 水题集锦

    最近刷银组刷得好欢快,好像都是水题,在这里吧他们都记录一下吧(都是水题大家一定是道道都虐的把= =)几道比较神奇的题到时再列出来单独讲一下吧= =(其实我会说是BZOJ蹦了无聊再来写的么 = =) [ ...

  2. usaco 2010年3月银组题解

    usaco银组解题报告 一.石子游戏如果把‘O’当作0,‘X’当做1,则N个洞的每一种状态都可以看做是一个N位二进制数.于是,这个问题就变成了求环绕的N位格雷码.幸运的是,这个结构很容易就能够用一个简 ...

  3. Bootstrap -- 下拉菜单、输入框组、导航菜单

    Bootstrap -- 下拉菜单.输入框组.导航菜单 1. 下拉菜单 可以使用带有各种大小按钮的下拉菜单:.btn-lg..btn-sm 或 .btn-xs. 实现下拉菜单: <!DOCTYP ...

  4. Android典型界面设计(3)——访网易新闻实现双导航tab切换

    一.问题描述 双导航tab切换(底部区块+区域内头部导航),实现方案底部区域使用FragmentTabHost+Fragment, 区域内头部导航使用ViewPager+Fragment,可在之前博客 ...

  5. Android典型界面设计(7) ——DrawerLayout+Fragement+ViewPager+PagerTabStrip实现双导航

    一.问题描述 在Android典型界面设计(3)的我们实现了双导航效果,即外层底部导航和内部区域的头部导航,如网易新闻等很多应用采用了这种导航,但Google提供DrawerLayout可实现抽屉式导 ...

  6. Android典型界面设计-访网易新闻实现双导航tab切换

    一.问题描述 双导航tab切换(底部区块+区域内头部导航),实现方案底部区域使用FragmentTabHost+Fragment, 区域内头部导航使用ViewPager+Fragment,可在之前博客 ...

  7. bootstrap学习笔记<十>(按钮组,导航)

    1)按钮组.样式:class="btn-group" <div class="btn-group"> <button class=" ...

  8. bootstrap输入框组、导航和导航条

    输入框组(input groups) 避免使用select  支持不好,使用输入框组 尺寸根据  input-group-lg    input-group-sm来选择   <div class ...

  9. Android典型界面设计(8) ——ViewPager+PagerSlidingTabStrip实现双导航

    一.问题描述 PagerSlidingTabStrip是android开源项目,指示器控件.官网地址:https://github.com/astuetz/PagerSlidingTabStrip 该 ...

随机推荐

  1. PCI-CAN卡端口配置程序设计

    每个PCI-CAN卡实现了2个CAN通信通道,但共用一个PCI接口(9030).在windows驱动中,每一个PCI接口只能被某一个进程独占使用,这就意味着:映射在同一块板卡上的2个CAN通道,只能在 ...

  2. 记使用vim常用的快捷键

    显示行数  :set nu 跳转到最后一行 G 跳转到指定行数 nG  n为行数 跳转到行首 0 跳转到行尾 $ (shift+4)

  3. 进程间通信之管道--pipe和fifo使用

    匿名管道pipe 函数原型: #include <unistd.h> int pipe(int fildes[2]); 参数说明 fildes是我们传入的数组,也是一个传出参数.filde ...

  4. ValueError: Cannot assign "\<QuerySet [<Area: China>]\>": "Area.parent" must be a "Area" instance.

    在研究才Django自关联的过程中,在插入数据时爆出如下错误: ValueError: Cannot assign "<QuerySet [<Area: China>]&g ...

  5. 「CF525D」Arthur and Walls

    题目链接 戳我 \(Solution\) 如果一个#要更改,那么一个四个格子的正方形只有他一个是#,bfs弄一下就好了 \(Code\) #include<bits/stdc++.h> u ...

  6. IDEA:Process finished with exit code -1073741819 (0xC0000005)

    出门左转:https://www.cnblogs.com/virgosnail/p/10335224.html

  7. SRS之SrsRtmpConn::service_cycle详解

    1. SrsRtmpConn::service_cycle 当服务器在 conn 线程的开始调用 connect_app 函数接收并解析客户端发送的 connect 消息后,调用该 service_c ...

  8. flutter showModalBottomSheet max height

    static void showBuyServiceDialog(BuildContext context) { showModalBottomSheet( context: context, isS ...

  9. windows文件上传到linux服务器上

    https://blog.csdn.net/m0_37751917/article/details/80739850 1:检查是否安装sz  rz rpm -qa |grep sz rpm -qa | ...

  10. kvm简介及创建虚拟化安装(1)

    kvm虚拟化介绍 一.虚拟化分类 1.虚拟化,是指通过虚拟化技术将一台计算机虚拟为多台逻辑计算机.在一台计算机上同时运行多个逻辑计算机,每个逻辑计算机可运行不同的操作系统,并且应用程序都可以在相互独立 ...