【模板】Johnson最短路_luoguP5905

算法作用

用来解决带负权的有向图的最短路问题。

只要跑一次spfa,就可以随便跑Dij了。

算法思想

  • 给每条边重新安排一个边权,使得不再存在负权边,并且可以由新图的最短路结果快速推出原图的最短路结果。
  • 不连通的对每个连通块可以分别求。所以我们只要考虑联通的情况下怎么做。
  • 那么,我们可以回想一下k短路,在哪个里面我们有最短路树。而且图中非树边我们给他们赋予了一个新的权值,意义是如果加上这条边,最短路会变大多少。我们发现,这个值一定是正的。
  • 然后,就很好玩了。你会发现这个东西,很裂项。因为他的表达式是\(w_e+dis_{e_{from}}-dis_{e_{to}}\)。
  • 于是,就发现,所有\(i\)到\(j\)的路径,都是原来的路径长度再加上\(dis_i-dis_j\)。
  • 赢了。

代码

luoguP5905

#include<bits/stdc++.h>
#define LL long long
#define MAXN 3100
#define MAXM 6100
#define MAXNUM 10000000
#define INF 1000000000
using namespace std;
template<typename T>void Read(T &cn)
{
    char c;int sig = 1;
    while(!isdigit(c = getchar()))if(c == '-')sig = -1; cn = c-48;
    while(isdigit(c = getchar()))cn = cn*10+c-48; cn*=sig;
}
template<typename T>void Write(T cn)
{
    if(cn < 0) {putchar('-'); cn = 0-cn; }
    int wei = 0; T cm = 0; int cx = cn%10; cn/=10;
    while(cn)cm = cm*10+cn%10,cn/=10,wei++;
    while(wei--)putchar(cm%10+48),cm/=10;
    putchar(cx+48);
}
struct qwe{
    int a,b,ne,w;
    void mk(int cn, int cm, int cx, int cw) {a = cn; b = cm; ne = cx; w = cw; }
};
struct qwer{
    int a,b;
    void mk(int cn, int cm) {a = cn; b = cm; }
    inline friend bool operator <(qwer a, qwer b) {return a.b > b.b; }
};
qwe a[MAXM+MAXN+1];
int alen;
int head[MAXN+10];
int n,m,p;
int vis[MAXN+1], lu[MAXN+1], dui[MAXNUM+1], l, r;
int ci[MAXN+1];
int lu2[MAXN+1];
qwer dui2[MAXM+MAXN+1];
void lian(int cn, int cm, int cx) {a[++alen].mk(cn,cm,head[cn],cx); head[cn] = alen; }
void spfa(int cn)
{
    l = r = 0;
    memset(vis,0,sizeof(vis)); memset(ci,0,sizeof(ci));
    dui[++r] = cn;
    vis[0] = 1;
    while(l < r)
    {
        int dang = dui[++l]; vis[dang] = 0;
        ci[dang]++; if(ci[dang] > n) {p = 1; return; }
        for(int i = head[dang];i;i = a[i].ne)
        {
            int y = a[i].b;
            if(lu[y] <= lu[dang] + a[i].w) continue;
            lu[y] = lu[dang] + a[i].w;
            if(!vis[y]) dui[++r] = y, vis[y] = 1;
        }
    }
}
void dij(int cn, qwer dui[], int lu[])
{
    for(int i = 1;i<=n;i++) lu[i] = INF;
    lu[cn] = 0; int dlen = 0; dui[++dlen].mk(cn,0);
    while(dlen)
    {
        while(dlen && dui[1].b != lu[dui[1].a]) pop_heap(dui+1,dui+(dlen--)+1);
        if(!dlen) break;
        int dang = dui[1].a; pop_heap(dui+1,dui+(dlen--)+1);
        for(int i = head[dang];i;i = a[i].ne)
        {
            int y = a[i].b;
            if(lu[y] <= lu[dang] + a[i].w || !y) continue;
            lu[y] = lu[dang] + a[i].w;
            dui[++dlen].mk(y,lu[y]); push_heap(dui+1,dui+dlen+1);
        }
    }
}
int main()
{
    Read(n); Read(m);
    alen = 0; memset(vis,0,sizeof(vis));
    for(int i = 1;i<=m;i++) {int bx,by,bz; Read(bx); Read(by); Read(bz); lian(bx,by,bz); }
    p = 0; for(int i = 1;i<=n;i++) lu[i] = INF;
    for(int i = 1;i<=n;i++) {if(lu[i] == INF) lu[i] = 0, spfa(i); if(p) {puts("-1"); return 0; } }
    for(int i = 1;i<=m;i++) a[i].w += lu[a[i].a] - lu[a[i].b];
    for(int i = 1;i<=n;i++)
    {
        dij(i,dui2,lu2);
        LL ans = 0;
        for(int j = 1;j<=n;j++) ans = ans + 1ll*j*(lu2[j] == INF ? INF : (lu2[j] - lu[i] + lu[j]));
        Write(ans); puts("");
    }
    return 0;
}

原文地址:https://www.cnblogs.com/czyarl/p/12332827.html

时间: 2024-08-03 16:46:39

【模板】Johnson最短路_luoguP5905的相关文章

模板(最短路,最小生成树,并查集)

单源最短路 #include<queue> #include<cstdio> #define INF 2147483647LL using namespace std; struct node { int to,dis,next; }; struct node edge[500005]; int n,m,num,head[10001],dis_1[10001]; inline void edge_add(int from,int to,int dis) { num++; edge[

luogu2483 【模板】k短路([SDOI2010]魔法猪学院)

模板题 #include <iostream> #include <cstring> #include <cstdio> #include <queue> using namespace std; int n, m, hea[5005], cnt, uu, vv, ans; double e, ww, dis[5005]; const double eps=1e-7; bool vis[5005]; struct Edge{ int too, nxt; do

P2483 【模板】k短路([SDOI2010]魔法猪学院)

题目描述 iPig在假期来到了传说中的魔法猪学院,开始为期两个月的魔法猪训练.经过了一周理论知识和一周基本魔法的学习之后,iPig对猪世界的世界本原有了很多的了解:众所周知,世界是由元素构成的:元素与元素之间可以互相转换:能量守恒--. 能量守恒--iPig 今天就在进行一个麻烦的测验.iPig 在之前的学习中已经知道了很多种元素,并学会了可以转化这些元素的魔法,每种魔法需要消耗 iPig 一定的能量.作为 PKU 的顶尖学猪,让 iPig 用最少的能量完成从一种元素转换到另一种元素--等等,i

【模板】最短路计数

洛谷 1144 dijkstra+手写堆 #include<cstdio> #include<algorithm> #define N 2000010 #define Mod 100003 #define rg register using namespace std; int n,m,tot,dis[N],last[N],cnt[N],pos[N]; struct edge{ int to,pre,dis; }e[N]; struct heap{ int poi,dis; }h[

[CTSC2017]最长上升自序列(伪题解)(树状数组+DP套DP+最小费用最大流+Johnson最短路+Yang_Tableau)

部分分做法很多,但每想出来一个也就多5-10分.正解还不会,下面是各种部分分做法: Subtask 1:k=1 LCS长度最长为1,也就是说不存在j>i和a[j]>a[i]同时成立.显然就是一个LDS,树状数组直接求即可. Subtask 2:k=2 最多两个,也就是可以由两个LCS拼起来,f[i][j]表示第一个LCS以i结尾,第二个以j结尾的方案数,转移显然. Subtask 3:k=2 树状数组优化DP,复杂度由$O(n^3)$降为$O(n^2 \log n)$ Subtask 4,5:

最短路总结

最近过的最短路题目稍微总结一下,顺便写一下模板,最短路算法众多有floyd.dij.bell-man.spfa,速度最快就是dij+优先队列或者dij+堆排序,spfa理论上很快o(ke)但实际并不一定不过spfa传说中有一个很NB用处就是处理带负权回路. 邻接表VS邻接矩阵:根据写题经验,如果可以用矩阵那一定是首选,矩阵速度比表快而且题目出现多重边时矩阵很好解决. 今天不小心又遇见正向表与最短路图(HDU2433),感觉很兴奋哈,通宵刷题的感觉很好玩!遗憾的是hdu1385还没过,那是一题最短

[SCOI2007]k短路(A*)

题目描述 有nn个城市和mm条单向道路,城市编号为11到nn.每条道路连接两个不同的城市,且任意两条道路要么起点不同要么终点不同,因此nn和mm满足m \le n(n-1)m≤n(n?1). 给定两个城市a和b,可以给a到b的所有简单路(所有城市最多经过一次,包括起点和终点)排序:先按长度从小到大排序,长度相同时按照字典序从小到大排序.你的任务是求出a到b的第kk短路 输入格式 输入第一行包含五个正整数n, m, k, a, b. 以下m行每行三个整数u, v, l,表示从城市u到城市v有一条长

hdu 1217 Arbitrage (spfa算法)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1217 题目大意:通过货币的转换,来判断是否获利,如果获利则输出Yes,否则输出No. 这里介绍一个STL中的map容器去处理数据,map<string,int>V,M; 现在我目前的理解是将字符串转换成数字,然后就是根据spfa的模板找最短路了..哇哈哈( ⊙o⊙ )哇 1 #include <iostream> 2 #include <cstdio> 3 #include

2015安徽省赛 G.你来擒孟获

http://xcacm.hfut.edu.cn/problem.php?id=1211 SPFA模板题目 最短路变种,从起点终点各找一次最短路相加 #include<iostream> #include<vector> #include<deque> #include<cstdio> #include<cstring> using namespace std; struct Edge { int to,length; }; bool spfa(