题意:有 F 组数据,每组数据给 N, M, W,代表 N 个点,M 条无向边, W 条负权有向边。接下来 M 行( S, E, T), 代表从 S 和 E 之间有一条权值为 T 的无向边,接下来 W 行 ( S, E, T) ,代表从 S 到 E 有 一条权值为 -T 的边,注意是负的,问是否可以从某个点出发,经过一定的路径后,回到起点且权值为负。
解法:典型的求负环,只要有负环存在,那么就可以通过走负环获得更小的权值,直到出现负数,即题意中的回到过去。题图似乎是连通的,从任意节点出发即可。用 SPFA。注意构图时有向边和无向边的处理。
#include <iostream>
#include <cstring>
#include <string>
#include <queue>
#include <vector>
#include <cstdio>
using namespace std;
#define maxn 1003
#define INF 0x3f3f3f3f
struct Edge
{
int y,w;
Edge(int yy,int ww)
{
y = yy;
w = ww;
}
};
vector<Edge> ways[maxn];
int dis[maxn],cnt[maxn];
bool ever[maxn];
int n,m,worm;
void init()
{
memset(ways,0,sizeof(ways));
memset(cnt,0,sizeof(cnt));
memset(ever,0,sizeof(ever));
for(int i = 1;i <= n;++i)
{
dis[i] = INF;
}
dis[1] = 0;
for(int i = 0;i < m;++i)
{
int x,y,w;
scanf("%d%d%d",&x,&y,&w);
ways[x].push_back(Edge(y,w));
ways[y].push_back(Edge(x,w));
}
for(int i = 0;i < worm;++i)
{
int x,y,t;
cin>>x>>y>>t;
t = 0-t;
ways[x].push_back(Edge(y,t));
}
}
bool SPFA()
{
ever[1] = 1;
cnt[1]++;
queue<int> q;
q.push(1);
while(!q.empty())
{
int cur = q.front();
q.pop();
int len = ways[cur].size();
ever[cur] = 0;
for(int i = 0;i < len;++i)
{
int y = ways[cur][i].y;
if(ways[cur][i].w + dis[cur] < dis[y])
{
dis[y] = dis[cur] + ways[cur][i].w;
if(!ever[y])
{
q.push(y);
ever[y] = 1;
cnt[y]++;
if(cnt[y] >= n) return true;
}
}
}
}
return false;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&worm);
init();
bool flag = SPFA();
if(flag)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}