[HNOI2014]道路堵塞

题目描述

$A$ 国有 $N$座城市,依次标为$1$到$N$。同时,在这$N$座城市间有$M$条单向道路,每条道路的长度是一个正整数。现在,$A$国交通部指定了一条从城市$1$到城市$N$的路径,并且保证这条路径的长度是所有从城市$1$到城市$N$的路径中最短的。不幸的是,因为从城市$1$到城市$N$旅行的人越来越多,这条由交通部指定的路径经常发生堵塞。现在$A$国想知道,这条路径中的任意一条道路无法通行时,由城市$1$到$N$的最短路径长度是多少。

输入输出格式

输入格式:

输入文件第一行是三个用空格分开的正整数$N,\ M$和$L$,分别表示城市数目、单向道路数目和交通部指定的最短路径包含多少条道路。按下来$M$行,每行三个用空格分开的整数$a,\ b$和$c$,表示存在一条由城市$a$到城市$b$的长度为$c$的单向道路。这M行的行号也是对应道路的编号,即其中第$1$行对应的道路编号为$1$,第$2$行对应的道路编号为$2$,...,第M行对应的道路编号为$M$。最后一行为$L$个用空格分开的整数$sp_1$ ...,,$sp_L$,依次表示从城市$1$到城市$N$的由交通部指定的最短路径上的道路的编号。

输出格式:

输出文件包含$L$行,每行为一个整数,第$i$行$(i=1,\ 2, ...,L)$的整数表示删去编号为$sp_i$的道路后从城市1到城市N的最短路径长度。如果去掉后没有从城市$1$到城市$N$的路径,则输出$-1$。

输入输出样例

输入样例


输出样例:


主要是网上关于这题的题解分析太少了 希望能写一篇可以让大家看懂的……

这题是一道动态删边SPFA + 堆的题 说白了,就是SPFA乱搞

理论基础

首先,本题很友好的把一开始的最短路告诉了我们,且删掉的边都在最短路上,(正常动态删边,如果不是删的最短路上的边,直接输出最短路即可,所以本题就是动态删边最难搞的一部分,删了最短路上的边)

当你删掉一条边$(u -> u+1)$时,最短路必为 $1 -> x(x <= u) -> y(y >= u+1) -> n $其中有且仅有 $1 -> x$ 与 $y -> n $为原最短路上的路线

比如:

该图的最短路很明显为$1->3->4->5$

如果我们任意去掉一条最短路上的边$(1->3或3->4或4->5)$,很不巧,答案都是$1->2->5(Lenmin = 7)$,均满足$1 -> x(x <= u) -> y(y >= u+1) -> n$(好吧本图有点特殊)

搞定这个后,我们可以先把所有最短路上的点记录一下,并将其到1及到n的距离预处理出来

    st[] = pos[] = ;
    ;i<=L;i++)
    {
        scanf("%d",&path[i]);
        pos[ed[path[i]]] = i+;//该点在最短路上是第几个
        st[i+] = ed[path[i]];//最短路上第i+1个点是哪个点
    }
    ;i<=L;i++)f[i] = f[i-] + vlu[path[i-]];//Why not + -> L+1(n点):只删L条边 不可能从n点为起点
    ;i--) g[i] = g[i+] + vlu[path[i]]; // Why not 是 i -> 2 同 ↑

接着枚举每一条最短路上的边,删了它并分别做一次SPFA

重点来了,怎么SPFA!

首先是普通的$vis$和$dis$及$queue$

设起点为$u$

当搜到一个普通点时,和原SPFA一样,但如果搜到一个在最短路上的点且这个点在最短路上排在$u$的后面,就满足了上面的条件,有可能是最优解,我们可以把它放入一个栈$s$里,且打个标记该点在栈里;如果已经在栈里,只要更新它的$x$($tmp$数组中的$x$即为$value$,$id$为该点的编号)

最后搜完后将所有可能为答案的点及它所带来的答案加入到一个小根堆,然后弹出编号在$u$之前的点,则小根堆的top的答案即为最短路答案

可能会有的一些问题(有一些写为代码注释)

1.Why dis不用memset

如上图,第一条边做好后,$dis_2$是3,如果memset后,从3开始搜,$dis_2$就会变为5,就再也找不到1-2-5了;所以$dis_x$在本题的实质是以$1-x$(正在搜的点)为起点的最短的距离

2.在SPFA中,加入栈的都被特判过是在u之后,为什么还要弹出点

咳咳,同学,我们在搜下一条边之前并没有清空操作,有可能有是之前的答案但不符合现在的答案

3.……本人暂时没想到,不过有问题随便问

对了,上代码!

#include<bits/stdc++.h>
#define MAXN 110000
#define MAXM 210000
#define INF 2147483647
using namespace std;
int pos[MAXN],st[MAXN]; // pos[i] i点为原最短路上的第几个点 st[i]  最短路上的第i个点是谁
; // 普通邻接表
int dis[MAXN];
int path[MAXN]; // 原最短路
bool used[MAXN];//该点是否在栈里
int f[MAXN],g[MAXN]; // f[i]表示 1 - st[i] g[i] 表示 ed[i] - n
int n,m,L;
struct Point
{
    int x,id;
}tmp[MAXN];
inline bool operator < (const Point & A,const Point & B) {return A.x > B.x;}//变为小根堆
priority_queue < Point > Q;
inline void Line(int u,int v,int w) // 邻接表加边
{
    nxt[++l] = hed[u];
    hed[u] = l;
    ed[l] = v;
    vlu[l] = w;
}
inline int read()
{
    ;char ch = getchar();
    ') ch = getchar();
     + ch - ';ch = getchar();}
    return s;
}
inline void SPFA(int u)
{
    bool vis[MAXN];
    memset(vis,,sizeof(vis));
    memset(used,,sizeof(used));
    stack <int> s;
    queue <int> q;
    q.push(st[u]);
    vis[st[u]] = ;
    dis[st[u]] = f[u];
    while(!q.empty())
    {
        int k = q.front();
        q.pop();
        vis[k] = ;
        for(register int i = hed[k];i;i = nxt[i])
        {
            if(i == path[u]) continue;
            if(pos[ed[i]] > u)//可能是答案
            {
                if(!used[ed[i]])//将该点加入栈里
                {
                    used[ed[i]] = ;
                    s.push(ed[i]);
                    tmp[ed[i]].id = pos[ed[i]];
                    tmp[ed[i]].x = dis[k] + vlu[i] + g[pos[ed[i]]];
                }
                else tmp[ed[i]].x = min(tmp[ed[i]].x,dis[k] + vlu[i] + g[pos[ed[i]]]);//将该点的value更新
            }
            else if(dis[ed[i]] > dis[k] + vlu[i])//普通点
            {
                dis[ed[i]] = dis[k] + vlu[i];
                ;
            }
        }
    }
    while(!s.empty()) Q.push(tmp[s.top()]),s.pop();//将点出栈
}
int main()
{
    n = read(),m = read(),L = read();
    ;i<=m;i++)
    {
        int a = read(),b = read(),c = read();
        Line(a,b,c);
    }
    st[] = pos[] = ;
    ;i<=L;i++)
    {
        scanf("%d",&path[i]);
        pos[ed[path[i]]] = i+;
        st[i+] = ed[path[i]];
    }
    ;i<=L;i++) f[i] = f[i-] + vlu[path[i-]];//Why not + -> L+1(n点):只删L条边 不可能从n点为起点
    ;i--) g[i] = g[i+] + vlu[path[i]]; // Why not i -> 2 同 ↑
    ;i<=n;i++) dis[i] = INF;
    ;i<=L;i++)
    {
        SPFA(i);
        ,Q.pop();//将无用的点去掉
        if(Q.empty()) printf("-1\n");
        else printf("%d\n",Q.top().x);
    }
    ;
}

如果看完后有什么问题 一定要问呀!

动态删边SPFA: [HNOI2014]道路堵塞的更多相关文章

  1. 洛谷 [HNOI2014]道路堵塞 解题报告

    [HNOI2014]道路堵塞 题意 给一个有向图并给出一个这个图的一个\(1\sim n\)最短路,求删去这条最短路上任何一条边后的最短路. 又事SPFA玄学... 有个结论,新的最短路一定是\(1\ ...

  2. BZOJ.3575.[HNOI2014]道路堵塞(最短路 动态SPFA)

    题目链接 \(Description\) 给你一张有向图及一条\(1\)到\(n\)的最短路.对这条最短路上的每条边,求删掉这条边后\(1\)到\(n\)的最短路是多少. \(Solution\) 枚 ...

  3. bzoj 3575: [Hnoi2014]道路堵塞

    Description A 国有N座城市,依次标为1到N.同时,在这N座城市间有M条单向道路,每条道路的长度是一个正整数.现在,A国交通部指定了一条从城市1到城市N的路径, 并且保证这条路径的长度是所 ...

  4. [HNOI2014]道路堵塞

    题目描述 A国有N座城市,依次标为1到N.同时,在这N座城市间有M条单向道路,每条道路的长度是一个正整数.现在,A国交通部指定了一条从城市1到城市N的路径,并且保证这条路径的长度是所有从城市1到城市N ...

  5. bzoj3575[Hnoi2014]道路堵塞

    http://www.lydsy.com/JudgeOnline/problem.php?id=3575 总赶脚第二题总是比第三题难...... 好吧,这题一点思路都没有 听说用民科可以过掉大部分数据 ...

  6. luogu P3238 [HNOI2014]道路堵塞

    传送门 这什么题啊,乱搞就算了,不知道SPFA已经死了吗 不对那个时候好像还没死 暴力就是删掉边后跑Dijkstra SPFA 然后稍微分析一下,可以发现题目中要求的不经过最短路某条边的路径,一定是先 ...

  7. 【BZOJ】3575: [Hnoi2014]道路堵塞

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3575 大概的做法是,按照顺序枚举每一条要删去的边,(假设当前点为$u$,在最短路径上的下一 ...

  8. 【bzoj3575】 Hnoi2014—道路堵塞

    http://www.lydsy.com/JudgeOnline/problem.php?id=3575 (题目链接) 题意 给出一个有向图和一条最短路,问最短路上任意一条边断掉,此时的最短路是多少. ...

  9. 【LG3238】 [HNOI2014]道路堵塞

    题目描述 给你一张\(N\)个点.\(M\)条边的有向图,按顺序给定你一条有\(L\)条边的\(1\rightarrow n\)的最短路, 每次断掉这\(L\)条边中的一条(不对后面答案产生影响),求 ...

随机推荐

  1. 【转载】刘昕明:送给和我一样曾经浮躁过的PHP程序员

    刘昕明:送给和我一样曾经浮躁过的PHP程序员 来源:刘昕明博客 作者:刘昕明         2012年偶决定开始写博客了,不为别的,就希望可以通过博客记录我的成长历程同时也希望可以帮助一些刚毕业,刚 ...

  2. 1018: Give me the answer

    1018: Give me the answer 时间限制: 1 Sec  内存限制: 32 MB提交: 55  解决: 15[提交][状态][讨论版][命题人:外部导入] 题目描述 Farmer J ...

  3. python中那个断言assert的优化

    Python Assert 为何不尽如人意# Python中的断言用起来非常简单,你可以在assert后面跟上任意判断条件,如果断言失败则会抛出异常. Copy >>> assert ...

  4. 变量类型 ROWID 和 UROWID

    ROWID:    ROWID为该表行的唯一标识,是一个伪列,这个伪列可以用SELECT查看,但是不可以用INSERT, UPDATE来修改,不可以用DELETE来删除. UROWID: ROWID可 ...

  5. springboot整合activiti报错[processes/]不存在解决方案

    springboot整合activiti时,启动抛异常 nested exception is java.io.FileNotFoundException: class path resource [ ...

  6. linux数据库copy方法

    相信大多数程序员都会遇到数据库copy的问题,下面就总结几种常见的方法,针对有mysql基础的同学参考 方法一:利用sqlyog的copy database的功能,如图 这种最简单,速度比较慢: 方法 ...

  7. LeetCode970. 强整数

    问题:970. 强整数 用户通过次数0 用户尝试次数0 通过次数0 提交次数0 题目难度Easy 给定两个非负整数 x 和 y,如果某一整数等于 x^i + y^j,其中整数 i >= 0 且  ...

  8. 【牛客 错题集】Linux系统方面错题合集

    前言:牛客Linux322道全部刷完,有些题目较老,甚至考核5系统,现在7都出来了几年了 = = 还有些题目解析的很好部分也摘录了进来.很多涉及嵌入式开发的选择题同样的摘录的作为了解使用 ------ ...

  9. Shell学习——数值运算

    在Bash shell中,可以利用let.(( )).[]执行基本的算术操作,在高级操作时,使用expr和bc两个工具1.let[root@client02 ~]# no1=4[root@client ...

  10. Python入门必学:数据类型和变量的用法

    什么是数据类型?计算机顾名思义就是可以做数学计算的机器,因此,计算机程序理所当然地可以处理各种数值.但是,计算机能处理的远不止数值,还可以处理文本.图形.音频.视频.网页等各种各样的数据,不同的数据, ...