试题描述
输入数据给出一个有 N 个节点,M 条边的带权有向图。要求你写一个程序,判断这个有向图中是否存在负权回路。如果从一个点沿着某条路径出发,又回到了自己,而且所经过的边上的权和小于 0,就说这条路是一个负权回路。
如果存在负权回路,只输出一行 −1;如果不存在负权回路,再求出一个点S到每个点的最短路的长度。约定:S 到 S 的距离为
0,如果 S 与这个点不连通,则输出 NoPath。
输入
第一行三个正整数,分别为点数 N,边数 M,源点 S;
以下 M 行,每行三个整数 a,b,c,表示点 a,b之间连有一条边,权值为 c。
输出
如果存在负权环,只输出一行 −1,否则按以下格式输出:共 N 行,第 i 行描述 S 点到点 i 的最短路
如果 S 与 i 不连通,输出 NoPath;
如果 i=S,输出 0。
其他情况输出 S 到 i 的最短路的长度。
输入示例
6 8 1
1 3 4
1 2 6
3 4 -7
6 4 2
2 4 5
3 6 3
4 5 1
3 5 4
输出示例
0
6
4
-3
-2

先用DFS判断负环,然后跑最短路即可。

DFS具体判断负环的方法是:记录一个vis数组,表示在你这次搜索过程中有没有走到这个点,如果在搜索的时候,发现这个目标点vis==1了,并且你还可以更新这个目标点的dis,那么就存在负环。因为这就相当于你跑完一圈,dis又少了,那么这个环肯定是负的。

这里有一个优化:是大佬YSF、SYF和我们清华毕业的数学老师姚璐提出并证明的。一般判断负环时,我们会把dis初始值赋成INF,但是实际上,我们可以赋0。相当于DFS跑最短路的时候,遇到正边就不跑了,这样会节省很大的时间复杂度。那么怎么证明正确性呢?

首先,我们需要证明一点,对于一个负环,一定有一个点,从这个点到第起始点的所有边权都为负,因为如果有正的,dis为0的情况可能会被正的卡住。那么又怎么证明这个呢?

我们可以画一个函数图像,x坐标表示负环上的每个点,y表示到x点时经过的边权。画出来后,你会发现,一定有一个峰值,然后对于这个峰值建系,它右侧的值全都是负的了。

具体代码如下(注意我的dis赋成0也可以AC,也是一个上面说法的验证):

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <queue>
#define REP(i,k,n) for(int i=k;i<=n;i++)
#define in(a) a=read()
using namespace std;
inline int read(){
int x=,f=;
char ch=getchar();
for(;!isdigit(ch);ch=getchar())
if(ch=='-')
f=-;
for(;isdigit(ch);ch=getchar())
x=x*+ch-'';
return x*f;
}
int T;
int flag=;
int n,m,s;
int total=,nxt[],head[],to[],val[];
int vis[],dis[],book[];
int q[];
inline void adl(int a,int b,int c){
total++;
to[total]=b;
val[total]=c;
nxt[total]=head[a];
head[a]=total;
return ;
}
queue <int> Q;
inline void dfs(int u){
book[u]=;
for(int e=head[u];e;e=nxt[e]){
if(dis[to[e]]>dis[u]+val[e]){
dis[to[e]]=dis[u]+val[e];
if(vis[to[e]]){
flag=;
return ;
}
if(flag)
return ;
vis[to[e]]=;
dfs(to[e]);
vis[to[e]]=;
}
}
return ;
}
void SPFA(int st){
memset(vis,,sizeof(vis));
memset(dis,,sizeof(dis));
int hea=,tail=;
//q[hea]=st;
Q.push(st);
dis[st]=;
while(!Q.empty()/*tail>=hea*/){
int u=Q.front();
//hea++;
Q.pop();
vis[u]=;
for(int e=head[u];e;e=nxt[e])
if(dis[to[e]]>dis[u]+val[e]){
dis[to[e]]=dis[u]+val[e];
if(!vis[to[e]]){
vis[to[e]]=;
Q.push(to[e]);
}
}
}
return ;
}
int main()
{
total=flag=;
//memset(dis,127,sizeof(dis));
in(n);in(m);in(s);
int a,b,c;
REP(i,,m){
in(a);in(b);in(c);
adl(a,b,c);
}
REP(i,,n)
if(!book[i]){
memset(vis,,sizeof(vis));
dfs(i);
if(flag) break;
}
if(flag){
cout<<-<<endl;
return ;
}
SPFA(s);
REP(i,,n)
if(dis[i]>)
cout<<"NoPath"<<endl;
else cout<<dis[i]<<endl;
}

Loj10086 Easy SSSP的更多相关文章

  1. vijosP1053 Easy sssp

    vijosP1053 Easy sssp 链接:https://vijos.org/p/1053 [思路] SPFA. 题目中的陷阱比较多,但是只要中规中矩的写SPFA诸如:s与负圈不相连,有重边的情 ...

  2. Easy sssp

    Easy sssp 时间限制: 1 Sec  内存限制: 128 MB提交: 103  解决: 20[提交][状态][讨论版] 题目描述 输入数据给出一个有N(2  < =  N  < = ...

  3. Easy sssp(spfa)(负环)

    vijos    1053    Easy sssp 方法:用spfa判断是否存在负环 描述 输入数据给出一个有N(2 <= N <= 1,000)个节点,M(M <= 100,00 ...

  4. SPFA_YZOI 1662: Easy sssp

    题目描述 输入数据给出一个有N(2  < =  N  < =  1,000)个节点,M(M  < =  100,000)条边的带权有向图.  要求你写一个程序,  判断这个有向图中是 ...

  5. Vijos1053 Easy sssp[spfa 负环]

    描述 输入数据给出一个有N(2 <= N <= 1,000)个节点,M(M <= 100,000)条边的带权有向图. 要求你写一个程序, 判断这个有向图中是否存在负权回路. 如果从一 ...

  6. vijos 1053 Easy sssp

    描述 输入数据给出一个有N(2 <= N <= 1,000)个节点,M(M <= 100,000)条边的带权有向图. 要求你写一个程序, 判断这个有向图中是否存在负权回路. 如果从一 ...

  7. Easy sssp(vijos 1053)

    描述 输入数据给出一个有N(2 <= N <= 1,000)个节点,M(M <= 100,000)条边的带权有向图. 要求你写一个程序, 判断这个有向图中是否存在负权回路. 如果从一 ...

  8. Vijos——T1053 Easy sssp

    https://vijos.org/p/1053 描述 输入数据给出一个有N(2 <= N <= 1,000)个节点,M(M <= 100,000)条边的带权有向图. 要求你写一个程 ...

  9. Easy sssp(spfa判负环与求最短路)

    #include<bits/stdc++.h> using namespace std; int n,m,s; struct node{ int to,next,w; }e[]; bool ...

随机推荐

  1. tensorflow.nn.bidirectional_dynamic_rnn()函数的用法

    在分析Attention-over-attention源码过程中,对于tensorflow.nn.bidirectional_dynamic_rnn()函数的总结: 首先来看一下,函数: def bi ...

  2. Tinyos Makerules解读

    Makerules 文件解读 位于/opt/tinyos-2.1.2/support/make #-*-Makefile-*- vim:syntax=make #$Id: Makerules,v 1. ...

  3. numpy 简介

    .caret, .dropup > .btn > .caret { border-top-color: #000 !important; } .label { border: 1px so ...

  4. linux用户修改用户shell

    要拒绝系统用户登录,可以将其shell设置为/usr/sbin/nologin或者/bin/false # usermod -s /usr/sbin/nologin username 或者 # use ...

  5. 利用pycharm运行scrapy以及scrapy的配置

    1.安装两个whl文件 https://pypi.python.org/pypi/Twisted 下载Twisted的whl文件 https://pypi.python.org/pypi/Scrapy ...

  6. ovirt系统磁盘删除后清理功能验证步骤

    测试步骤主要是针对ovirt系统磁盘的‘删除后清理’功能,如下图所示: 测试如下两种方式: 预置条件: 搭建iscsi服务器,且划分一个11G的盘 勾选删除后清理操作步骤:1 .在linux虚拟机 d ...

  7. LINUX内核中的机制OOM

    [概念] LINUX内核中有一个机制叫做OOM killer(Out Of Memery killer) 该机制监控内存占用过大,尤其是瞬间消耗大量内存的进程, 为了防止内存被耗尽,所以OOM kil ...

  8. Guava cache功能简介(转)

    原文链接:http://ifeve.com/google-guava-cachesexplained/ 范例 LoadingCache<Key, Graph> graphs = Cache ...

  9. /proc文件夹介绍

    Linux系统上的/proc目录是一种文件系统,即proc文件系统.与其它常见的文件系统不同的是,/proc是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,用户可以通过 ...

  10. 洛谷 P2036 Perket 题解

    题目传送门 这道题可以使用dfs深搜实现,在每次递归深搜时要更新ans. #include<bits/stdc++.h> using namespace std; ,s=,b; ]; st ...