题目链接:http://poj.org/problem?id=3259
解题思路:只要图中存在某个圈,沿着这个圈走一圈之后时间能够回退(即负环),那么一定可以完成题目所说之事。
早期floyd做法
做法判断是否有节点到自身的最短路小于0
1829ms
///*** Floyed 法求任意两点最短路
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int dp[505][505],n;
void solve()
{
for (int k=1;k<=n;k++)
for (int i=1;i<=n;i++){
for (int j=1;j<=n;j++)
if (dp[i][j]>dp[i][k]+dp[k][j])
dp[i][j]=dp[i][k]+dp[k][j];
if (dp[i][i]<0) { cout<<"YES"<<endl; return ;}
}
cout<<"NO"<<endl;
}
int main()
{
std::ios::sync_with_stdio(false);
int t,m,s;
cin>>t;
while (t--){
cin>>n>>m>>s;
memset(dp,0x3f3f3f3f,sizeof(dp));
for (int i=1;i<=n;i++) dp[i][i]=0;
while (m--){
int u,v,w;
cin>>u>>v>>w;
if (w<dp[u][v])dp[u][v]=dp[v][u]=w;
}
while (s--){
int u,v,w;
cin>>u>>v>>w;
dp[u][v]=-w;
}
solve();
}
return 0;
}
Bellman_ford 做法
节点数为V,假设第V次更新数组仍然可以缩小,证明有负环
329ms
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<set>
#include<map>
#include<algorithm>
using namespace std;
#define ll long long
#define ull unsigned long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i<b;i++)
#define rof1(i,a,b) for (int i=a;i>=b;i--)
#define rof0(i,a,b) for (int i=a;i>b;i--)
#define pb push_back
#define fi first
#define se second
#define debug(x) printf("----Line %s----\n",#x)
#define pt(x,y) printf("%s = %d\n",#x,y)
#define INF 0x3f3f3f3f
#define dfl(x) ll x;scanf("%I64d",&x)
#define df2l(x,y) ll x,y;scanf("%I64d %I64d",&x,&y)
#define df(x) int x;scanf("%d",&x);
#define df2(x,y) int x,y;scanf("%d %d",&x,&y)
#define mod 1000000007
#define duozu(T) int T;scanf("%d",&T);while (T--)
const int N = 500;
struct Edge
{
int to,last,w;
}edge[2500*2+200+5];
int id,head[505];
void add(int u,int v,int w){edge[id].to = v;edge[id].w = w;edge[id].last = head[u];head[u] = id++;}
int dis[N];
bool Bellman_ford(int V)
{
memset(dis,INF,sizeof dis);
dis[1] = 0;
for1(k,1,V){
for1(u,1,V){
for (int j=head[u];j!=0;j=edge[j].last){
int v = edge[j].to;
if (dis[v]>dis[u]+edge[j].w){
dis[v] = dis[u]+edge[j].w;
if (k==V) return true;
}
}
}
}
return false;
}
int main()
{
//freopen("C:/Users/DELL/Desktop/input.txt", "r", stdin);
//freopen("C:/Users/DELL/Desktop/output.txt", "w", stdout);
duozu(T){
id = 1;
memset(head,0,sizeof head);
int n,m,mm;
scanf("%d %d %d",&n,&m,&mm);
int u,v,w;
for0(i,0,m){
scanf("%d %d %d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
for0(i,0,mm){
scanf("%d %d %d",&u,&v,&w);
add(u,v,-w);
}
bool flag = Bellman_ford(n);
printf("%s\n",flag?"YES":"NO");
}
return 0;
}
SPFA做法(队列优化的bellman_ford算法)
优化原因:只将上一轮松弛过的点入队列,没松弛过的点不会对其他点更新就不用进队列了
思路同bellman,一个点被修改V次,证明有负环
94ms
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<set>
#include<map>
#include<algorithm>
using namespace std;
#define ll long long
#define ull unsigned long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i<b;i++)
#define rof1(i,a,b) for (int i=a;i>=b;i--)
#define rof0(i,a,b) for (int i=a;i>b;i--)
#define pb push_back
#define fi first
#define se second
#define debug(x) printf("----Line %s----\n",#x)
#define pt(x,y) printf("%s = %d\n",#x,y)
#define INF 0x3f3f3f3f
#define dfl(x) ll x;scanf("%I64d",&x)
#define df2l(x,y) ll x,y;scanf("%I64d %I64d",&x,&y)
#define df(x) int x;scanf("%d",&x);
#define df2(x,y) int x,y;scanf("%d %d",&x,&y)
#define mod 1000000007
#define duozu(T) int T;scanf("%d",&T);while (T--)
const int N = 500+5;
const int M = 5200+5;
struct Edge
{
int to,last,w;
}edge[M];
int id,head[N];
void add(int u,int v,int w)
{
edge[id].to = v;
edge[id].w = w;
edge[id].last = head[u];
head[u] = id++;
}
void init()
{
id = 1;
memset(head,0,sizeof head);
}
int dis[N];
int val[N];
bool SPFA(int V)//V表示节点数
{
memset(dis,INF,sizeof dis);
memset(val,0,sizeof val);
int root = 1;
bool flag = false;
dis[root] = 0;
queue<int>que;
que.push(1);
while (!que.empty()){
int now = que.front();que.pop();
int v;
for (int i=head[now];i!=0;i=edge[i].last){
v = edge[i].to;
if (dis[v]>dis[now]+edge[i].w){
dis[v] = dis[now] + edge[i].w;
que.push(v);
val[v]++;
if (val[v]==V) {flag = true;break;}
}
}
if (flag) break;
}
return flag;
}
int main()
{
duozu(T){
int n,m,mm;
init();
scanf("%d %d %d",&n,&m,&mm);
int u,v,w;
for0(i,0,m){
scanf("%d %d %d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
for0(i,0,mm){
scanf("%d %d %d",&u,&v,&w);
add(u,v,-w);
}
bool flag = SPFA(n);
printf("%s\n",flag?"YES":"NO");
}
return 0;
}
SPFA优化
优化原因:被松弛后较小的点对其他点松弛的影响更大,先用这些点更新可以使得进入队列的元素更少
判负环思路同上
313ms(...C++deque太慢了,后来手动模拟了一下deque到了100ms左右,可能本题节点数太少优化不明显)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<set>
#include<map>
#include<algorithm>
using namespace std;
#define ll long long
#define ull unsigned long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i<b;i++)
#define rof1(i,a,b) for (int i=a;i>=b;i--)
#define rof0(i,a,b) for (int i=a;i>b;i--)
#define pb push_back
#define pf push_front
#define fi first
#define se second
#define debug(x) printf("----Line %s----\n",#x)
#define pt(x,y) printf("%s = %d\n",#x,y)
#define INF 0x3f3f3f3f
#define dfl(x) ll x;scanf("%I64d",&x)
#define df2l(x,y) ll x,y;scanf("%I64d %I64d",&x,&y)
#define df(x) int x;scanf("%d",&x);
#define df2(x,y) int x,y;scanf("%d %d",&x,&y)
#define mod 1000000007
#define duozu(T) int T;scanf("%d",&T);while (T--)
const int N = 500+5;
const int M = 5200+5;
struct Edge
{
int to,last,w;
}edge[M];
int id,head[N];
void add(int u,int v,int w)
{
edge[id].to = v;
edge[id].w = w;
edge[id].last = head[u];
head[u] = id++;
}
void init()
{
id = 1;
memset(head,0,sizeof head);
}
int dis[N];
int val[N];
bool SPFA(int V)//V表示节点数
{
memset(dis,INF,sizeof dis);
memset(val,0,sizeof val);
int root = 1;
bool flag = false;
dis[root] = 0;
deque<int>que;
que.pb(1);
while (!que.empty()){
int now = que.front();que.pop_front();
int v;
for (int i=head[now];i!=0;i=edge[i].last){
v = edge[i].to;
if (dis[v]>dis[now]+edge[i].w){
dis[v] = dis[now] + edge[i].w;
que.pb(v);
if (dis[que.front()]<dis[que.back()]){que.pf(v);que.pop_back();}
val[v]++;
if (val[v]==V) {flag = true;break;}
}
}
if (flag) break;
}
return flag;
}
int main()
{
duozu(T){
int n,m,mm;
init();
scanf("%d %d %d",&n,&m,&mm);
int u,v,w;
for0(i,0,m){
scanf("%d %d %d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
for0(i,0,mm){
scanf("%d %d %d",&u,&v,&w);
add(u,v,-w);
}
bool flag = SPFA(n);
printf("%s\n",flag?"YES":"NO");
}
return 0;
}