一、题目描述
寻找一个从顶点 1 所能到达的负环,负环定义为:一个边权之和为负的环。
输入格式
- 本题有多组数据
- 第一行一个正整数 TT 表示数据组数,对于每组数据:
- 第一行两个正整数 N,M,表示图有 N 个顶点,M 条边
接下来 MM 行,每行三个整数 a, b, wa,b,w,表示a \rightarrow ba→b 有一条权值为 ww 的边(若 w<0w<0 则为单向,否则双向)。
输出格式
- 共 T 行。对于每组数据,存在负环则输出一行 YES,否则输出一行 NO。
说明/提示
-
,
, 。
2
3 4
1 2 2
1 3 4
2 3 1
3 1 -3
3 3
1 2 3
2 3 4
3 1 -8
NO
YES
二、题解
方法一:Bellman-Ford
题意:寻找一个从顶点 1 所能到达的负环。而常规的 Bellman-Ford 算法判断的是图中是否有负环,也就是说如果 1 号结点与负环不连通按理说我们应该输出 "NO"
。
加边错误以及疑惑::
- 为什么之前加了 1 条边会得 0 分,现在如果 cost >= 0 需要再加 1 条平行边就能的 72 分?
import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
static int V, E;
static int[] dist, count;
static boolean[] inq;
static List<Edge>[] graph;
static int INF = 0x3f3f3f3f;
private static List<Edge>[] createGraph(int V){
List<Edge>[] graph = new List[V+1];
for (int i = 0; i <= V; i++)
graph[i] = new ArrayList<>();
return graph;
}
private static void addEdge(int from, int to, int cost) {
graph[from].add(new Edge(to, cost));
}
private static boolean spfa(int S) {
Queue<Integer> q = new ArrayDeque<>();
q.offer(S);
inq[S] = true;
dist[S] = 0;
for (int i = 1; i <= V; i++) {
q.add(i);
inq[i] = true;
}
while (!q.isEmpty()) {
Integer cur = q.poll();
inq[cur] = false;
for (int i = 0; i < graph.length; i++)
for (Edge edge : graph[i]) {
if (dist[edge.to] > dist[i] + edge.cost) {
dist[edge.to] = dist[i] + edge.cost;
count[edge.to] = count[i] + 1;
if (count[edge.to] >= V)
return true;
if (!inq[edge.to]) {
q.offer(edge.to);
inq[edge.to] = true;
}
}
}
}
return false;
}
public static void main(String[] args) throws IOException {
Scanner sc = new Scanner(new BufferedInputStream(System.in));
BufferedWriter w = new BufferedWriter(new OutputStreamWriter(System.out));
int T = sc.nextInt();
while (T-- > 0) {
V = sc.nextInt();
E = sc.nextInt();
inq = new boolean[V+1];
graph = createGraph(V+1);
dist = new int[V+1];
count = new int[V+1];
for (int i = 0; i < E; i++) {
int from = sc.nextInt();
int to = sc.nextInt();
int cost = sc.nextInt();
addEdge(from, to, cost);
if (cost >= 0)
addEdge(to, from, cost);
}
System.out.println(spfa(1) ? "YES" : "NO");
}
}
static class Edge {
int to, cost;
public Edge(int _to, int _cost) {
to = _to;
cost = _cost;
}
}
}
复杂度分析
- 时间复杂度: ,
- 空间复杂度: ,
方法二:链式前向星
边界出错,哭了:
- 现在一般的内存限制都够用的,所以直接定义 MAXN = 上限 即可,不用取考虑边界问题。
import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
static int V, E;
static int[] dist, count;
static int INF = 0x3f3f3f3f;
static boolean[] inq;
static int[] head;
static Edge[] edges;
static int tot;
static int MAXN = 100000;
private static void addEdge(int u, int v, int w) {
edges[tot] = new Edge();
edges[tot].to = v;
edges[tot].w = w;
edges[tot].next = head[u];
head[u] = tot++;
}
private static boolean spfa(int S) {
dist[S] = 0;
Queue<Integer> q = new ArrayDeque<>();
q.add(S);
inq[S] = true;
while (!q.isEmpty()) {
int v = q.poll();
inq[v] = false;
for (int i = head[v]; i != 0; i = edges[i].next) {
int to = edges[i].to, w = edges[i].w;
if (dist[to] > dist[v] + w) {
count[to] = count[v] + 1;
if (count[to] >= V)
return true;
dist[to] = dist[v] + w;
if (!inq[to]) {
q.add(to);
inq[to] = true;
}
}
}
}
return false;
}
public static void main(String[] args) throws IOException {
Scanner sc = new Scanner(new BufferedInputStream(System.in));
BufferedWriter w = new BufferedWriter(new OutputStreamWriter(System.out));
int T = sc.nextInt();
while (T-- > 0) {
V = sc.nextInt();
E = sc.nextInt();
inq = new boolean[V+1];
dist = new int[V+1];
count = new int[V+1];
head = new int[V+1];
edges = new Edge[MAXN];
Arrays.fill(dist, INF);
for (int i = 0; i < E; i++) {
int from = sc.nextInt();
int to = sc.nextInt();
int cost = sc.nextInt();
addEdge(from, to, cost);
if (cost >= 0) {
addEdge(to, from, cost);
}
}
System.out.println(spfa(1) ? "YES" : "NO");
}
}
static class Edge {
int to, w, next;
public Edge() { }
public Edge(int to, int w, int next) {
this.to = to;
this.w = w;
this.next = next;
}
}
}
复杂度分析
- 时间复杂度: ,
- 空间复杂度: ,