图论训练之三

https://www.luogu.org/problem/P2149

算是找到了一道好题吧

看样例直接提醒我们两点间的最短路可能不止一条

套路

如何判断一条边是否在这两点(S,T)最短路径上

solution:S跑一遍単源最短路,T跑一遍単源最短路,再枚举边,判断!!!!!

至于为什么要跑两遍,很好理解,因为他是単源最短路,

自行体会,实在不行看代码就懂了

同理这一道题,分别四个点跑一次単源最短路,判断两次就可以

但问题来了,因为最短路可能不止一条,

这样你就不可能把所以成立的边都加起来

那就要拓扑排序

首先我们可以证明一个结论

两对点最短路的最长公共路径一定是一条连续的链
为什么?因为假如出现开始相交了又分开又相交的情况的话

证明

只有可能分开后的两条路径长度相等,

如果不等的话就不叫最短路了

既然只有相等,那分开的那条也属于另一条的最短路经,

后来是会考虑到的

所以再在新建的图里走一次拓扑排序即可

code(码风比较傻逼,不是我写的,思路清晰,能看就行):

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<utility>
#include<iostream>
#include<queue>
#include<string>
#define inf 0x3f3f3f3f
#define mp make_pair
#define maxn 1505
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
struct edge{
    int to,cst;
}el[maxn*maxn],el2[maxn*maxn];
int E,n,m,head[maxn],nxt[maxn*maxn],x1,y1,x2,y2,d[5][maxn];
int E2,head2[maxn],nxt2[maxn*maxn],len[maxn],deg[maxn],que[maxn],he,ta;
//变量名带2的都是新建的图的信息。
bool vis[maxn];
inline int getint(){
    char c;
    for(c=getchar();c<'0' || c>'9';c=getchar());
    int res = c - '0';
    for(c=getchar();c>='0'&&c<='9';c=getchar()) res = res * 10 + (c - '0');
    return res;
}
inline void addedge2(int u,int v,int w){
    E2++;
    el2[E2] = (edge){v,w};
    nxt2[E2] = head2[u];
    head2[u] = E2;
    deg[v]++;
}
inline void addedge(int u,int v,int w){
    E++;
    el[E] = (edge){v,w};
    nxt[E] = head[u];
    head[u] = E;
}
//这么古老的最短路(不用堆优化的)
inline void dijkstra(int id,int S){
    memset(d[id],0x3f,sizeof(d[id]));
    memset(vis,0,sizeof(vis));
    d[id][S] = 0;
    for(int i=1;i<=n;i++){
        int md = inf,u = -1;
        for(int j=1;j<=n;j++){
            if(!vis[j] && md > d[id][j]){
                md = d[id][j];
                u = j;
            }
        }
        if(u == -1) break;
        vis[u] = true;
        for(int j=head[u];j!=-1;j=nxt[j]){
            d[id][el[j].to] = min(d[id][el[j].to],d[id][u] + el[j].cst);
        }
    }
}
inline void quepush(int x){
    que[ta] = x;
    ta++;
}
inline int quepop(){
    int ret = que[he];
    he++;
    return ret;
}
inline void topo(){
        memset(vis,0,sizeof(vis));
    he = ta = 1;
    for(int i=1;i<=n;i++) if(!deg[i]) quepush(i);
    while(he != ta){
        int u = quepop();
        for(int i=head2[u];i!=-1;i=nxt2[i]){
            deg[el2[i].to]--;
            len[el2[i].to] = max(len[el2[i].to],len[u] + el2[i].cst);
            if(deg[el2[i].to] == 0) quepush(el2[i].to);
        }
    }
}
int main(){
    memset(head,-1,sizeof(head));
    memset(head2,-1,sizeof(head2));
    n = getint();
    m = getint();
    x1=getint(),y1=getint(),x2=getint(),y2=getint();
    for(int i=1;i<=m;i++){
        int u,v,w;
        u=getint(),v=getint(),w=getint();
        addedge(u,v,w);
        addedge(v,u,w);
    }
    dijkstra(1,x1);
    dijkstra(2,y1);
    dijkstra(3,x2);
    dijkstra(4,y2);
    for(int i=1;i<=n;i++){
        for(int j=head[i];j!=-1;j=nxt[j]){
            if(d[1][i] + el[j].cst + d[2][el[j].to] == d[1][y1]){
                if(d[3][i] + el[j].cst + d[4][el[j].to] == d[3][y2])
                addedge2(i,el[j].to,el[j].cst);
            }
        }
    }
    topo();
    int ans = 0;
    for(int i=1;i<=n;i++) ans = max(ans,len[i]);
    memset(head2,-1,sizeof(head2));
    E2 = 0;
    memset(deg,0,sizeof(deg));
    memset(len,0,sizeof(len));
    for(int i=1;i<=n;i++){
        for(int j=head[i];j!=-1;j=nxt[j]){
            if(d[1][i] + el[j].cst + d[2][el[j].to] == d[1][y1]){
                if(d[4][i] + el[j].cst + d[3][el[j].to] == d[3][y2])
                addedge2(i,el[j].to,el[j].cst);
//正向建边
            }
        }
    }
    topo();
    for(int i=1;i<=n;i++) ans = max(ans,len[i]);
    printf("%d\n",ans);
  return 0;
}

原文地址:https://www.cnblogs.com/wzxbeliever/p/11624794.html

时间: 2024-10-09 16:38:16

图论训练之三的相关文章

字符串训练之三

字符串训练三 https://www.luogu.org/problem/P4551 题目描述: 给定一棵n个点的带权树,结点下标从1开始到N.寻找树中找两个结点,求最长的异或路径. 异或路径指的是指两个结点之间唯一路径上的所有边权的异或 分析: 嗯?这不是个图论题吗?什么狗屁字符串? 首先看到异或,那01trie树就必不可少的了 首先对一条边异或2次,相当于没有异或. 这样的话 i -> j 的异或和,就是 i -> 1 的异或和,再异或上 1 -> j 的异或和. 处理出每个点到1路

[图论训练]BZOJ 3245: 最快路线【最短路】

Description 精 明的小R每每开车出行总是喜欢走最快路线,而不是最短路线.很明显,每条道路的限速是小R需要考虑的关键问题.不过有一些限速标志丢失了,于是小R将不知 道能开多快.不过有一个合理的方法是进入这段道路时不改变速度行驶.你的任务就是计算从小R家(0号路口)到D号路口的最快路线. 现在你得到了这个城市的地图,这个地图上的路都是单向的,而且对于两个路口A和B,最多只有一条道路从A到B.并且假设可以瞬间完成路口的转弯和加速. Input 第一行是三个整数N,M,D(路口数目,道路数目

[图论训练]1143: [CTSC2008]祭祀river 二分图匹配

Description 在遥远的东方,有一个神秘的民族,自称Y族.他们世代居住在 水面上,奉龙王为神.每逢重大庆典, Y族都会在水面上举办盛大的祭祀活动.我们可以把Y族居住地水系看成一个由岔口和河道组成的网络.每条河道连接着两个岔口,并且水在河道内按照一个固定的 方向流动.显然,水系中不会有环流(下图描述一个环流的例子). 由于人数众多的原因,Y族的祭祀活动会在多个岔口上同时举行.出于对龙王的尊重,这些祭祀地点的选择必须非常慎重.准确地说,Y族人认为,如果水流 可以从一个祭祀点流到另外一个祭祀点

[图论训练]BZOJ 1624: [Usaco2008 Open] Clear And Present Danger 寻宝之路【floyd】

Description 农夫约翰正驾驶一条小艇在牛勒比海上航行. 海上有N(1≤N≤100)个岛屿,用1到N编号.约翰从1号小岛出发,最后到达N号小岛.一 张藏宝图上说,如果他的路程上经过的小岛依次出现了 Ai,A2,…,AM(2≤M≤10000)这样的序列(不一定相邻),那他最终就能找到古老的宝藏.  但是,由于牛勒比海有海盗出没.约翰知道任意两 个岛屿之间的航线上海盗出没的概率,他用一个危险指数Dij(0≤Dij≤100000)来描述.他希望他的寻宝活动经过的航线危险指数之和最小.那么, 在

图论训练

Light OJ 1002 - Country Roads 1.Dijkstra 1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N=555; 5 int d[N]; 6 bool vis[N]; 7 const int INF=0x3f3f3f3f; 8 vector < pair<int,int> > E[N]; 9 10 void init(){ 11 for(int i=0;i<

图论训练之一

noip水题系列 https://www.luogu.org/problem/P1027 题目明显就是一个多组数据(也就<=10)单源最短路, 这里可以就不用floyed 但你会发现它是道蓝题也是有一定道理的 你会很恼火它的建边: 使用勾股定理加一系列的特判,而且又是浮点数 话不多述, 思路简单,代码麻烦,就把题解修改一下搬过来了 code: #include <cstdio> #include <cmath> #include <cstring> #includ

图论训练之二

https://www.luogu.org/problem/P1613 题目描述 小A的工作不仅繁琐,更有苛刻的规定,要求小A每天早上在6:00之前到达公司,否则这个月工资清零.可是小A偏偏又有赖床的坏毛病.于是为了保住自己的工资,小A买了一个十分牛B的空间跑路器,每秒钟可以跑2^k千米(k是任意自然数).当然,这个机器是用longint存的,所以总跑路长度不能超过maxlongint千米.小A的家到公司的路可以看做一个有向图,小A家为点1,公司为点n,每条边长度均为一千米.小A想每天能醒地尽量

图论训练之四

https://www.luogu.org/problem/P2680 题意:n个点,n-1条边,边有边权,无向图,m条航线,0时刻同时启程,你可以将一个边权变为0,求使得最后一个航线到达最少花费时间. 这道题很早就做过了,但现在又忘了, 而且觉得这是一道好题,所以写一篇博客 分析: 首先最大值最小,二分毋庸置疑 当然是二分答案,但怎么判断就是本题的难点了 明确,删边一定是在最长的路线中删去(很好理解吧) 在此基础上 看能否有其他的路线经过删的边就更好 普及一下树上差分 如果是点差分,(a,b)

杂题训练之三

https://www.luogu.org/problem/P1858 模板题,套路东西,思路代码里,很清晰,很明了 code : #include<bits/stdc++.h> #define IL inline #define RI register int using namespace std; IL void in(int &x) { int f=1;x=0;char s=getchar(); while(s>'9' or s<'0'){if(s=='-')f=-