描述
输入数据给出一个有N(
要求你写一个程序, 判断这个有向图中是否存在负权回路. 如果从一个点沿着某条路径出发, 又回到了自己, 而且所经过的边上的权和小于0, 就说这条路是一个负权回路.
如果存在负权回路, 只输出一行-1;
如果不存在负权回路, 再求出一个点S(
格式
输入格式
第一行: 点数N(
以下M行, 每行三个整数a, b, c表示点a, b(
输出格式
如果存在负权环, 只输出一行-1, 否则按以下格式输出
共N行, 第i行描述S点到点i的最短路:
如果S与i不连通, 输出NoPath;
如果i = S, 输出0;
其他情况输出S到i的最短路的长度.
题解
坑真够多的。也许出这道题的目的是让我们真正了解最短路的负环问题。
用spfa如果入队超过当前结点数,则说明有负环;
spfa松弛到起点时,也说明有负环;
负环可能与起点不在一个联通块内;
对每个点暴力spfa会T;
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int nil = 0, oo = 1000000000, maxn = 100005;
int n, m, S;
int e, pnt[1005], nxt[maxn], u[maxn], v[maxn], w[maxn];
int d[1005], cnt[1005], dis[1005], sum;
bool vis[1005], flag, cal[1005];
void add(int a, int b, int c)
{
u[++e] = a; v[e] = b; w[e] = c;
nxt[e] = pnt[a]; pnt[a] = e;
}
void init()
{
int a, b, c;
scanf("%d%d%d", &n, &m, &S);
for(int i = 1; i <= m; ++i)
{
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
}
int spfa(int s)
{
memset(vis, 0, sizeof(vis));
memset(d, 0x3f, sizeof(d));
memset(cnt, 0, sizeof(cnt));
queue <int> Q;
int ans = 0;
Q.push(s);
d[s] = 0;
vis[s] = cal[s] = true;
++ans; ++cnt[s];
while(!Q.empty())
{
int t = Q.front();
Q.pop();
vis[t] = false;
for(int j = pnt[t]; j != nil; j = nxt[j])
{
if(d[v[j]] > w[j] + d[t])
{
d[v[j]] = w[j] + d[t];
if(!vis[v[j]])
{
vis[v[j]] = true;
++cnt[t];
if(cnt[t] + sum == n)
{
flag = true;
return -1;
}
if(!cal[v[j]])
{
cal[v[j]] = true;
++ans;
}
Q.push(v[j]);
}
}
}
}
return ans;
}
void work()
{
sum += spfa(S);
if(flag || d[S] < 0)
{
puts("-1");
return;
}
memcpy(dis, d, sizeof(d));
for(int i = 1; i <= n; ++i)
{
if(!cal[i])
{
sum += spfa(i);
if(flag || d[i] < 0)
{
puts("-1");
return;
}
}
}
for(int i = 1; i <= n; ++i)
{
if(dis[i] > oo)
{
puts("NoPath");
}
else
{
printf("%d\n", dis[i]);
}
}
}
int main()
{
init();
work();
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/t14t41t/article/details/47339799