在判断图的方向的问题时,需要仔细看一下:有向图+无向图相当于有向图+双向有向图=有向图,在bellman-ford的松弛阶段只需要更新弧尾端点就行。无向图相当于双向的有向图 ,弧尾就是弧头。在这里解释一下松弛操作的问题:
1.对于有向弧a->b:我们的松弛操作实质上是用a的结点值和ab弧的权值来推导b的结点值
2.对于无向弧,我们实质上是同时松弛a->b和b->a,就是将无向弧当做两个有向弧来处理
在这个题中,最开始我的错误在于将大路和虫洞都当做无向弧来处理,
3.关于无向图和有向图的问题,关键是要理解题意。
在此题中,虫洞作为边权为负值的有向图,大路作为边权为正的无向图。利用bellman-ford算法对负圈的判定程序,对所有边松弛n-1次后还能松弛,说明存在负环即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=505;
const int maxm=2505<<1;
const int maxw=205;
const int inf=0x3f3f3f3f;
int dis[maxn];
int edge[maxm+maxw][2];//0表示起点,1表示终点
//有向图
int time[maxm+maxw];//时间,边的权值
int f,n,m,w,a,b,t,cnt;//cnt是边的数量
void init()
{
memset(dis,inf,sizeof(dis));
dis[1]=0;
cnt=0;
}
bool bellman_ford()//1表示有负圈
{
for(int i=1; i<n; i++)
{
int flag=1;
for(int j=0; j<cnt+w; j++)
{
int pp=edge[j][0];
int qq=edge[j][1];
if(dis[qq]>dis[pp]+time[j])
{
dis[qq]=dis[pp]+time[j];
flag=0;
}
}
if(flag)
return false;//不再进行松弛操作
}
for(int j=0; j<cnt+w; j++)
{
int pp=edge[j][0];
int qq=edge[j][1];
if(dis[qq]>dis[pp]+time[j])
return true;
}
return false;
}
int main()
{
scanf("%d",&f);
while(f--)
{
scanf("%d%d%d",&n,&m,&w);
init();
for(int i=0; i<m; i++)
{
scanf("%d%d%d",&a,&b,&t);
edge[cnt][0]=a;
edge[cnt][1]=b;
time[cnt]=t;
cnt++;//田间的大路加双边
edge[cnt][0]=b;
edge[cnt][1]=a;
time[cnt]=t;
cnt++;
}
for(int i=cnt; i<cnt+w; i++)
{
scanf("%d%d%d",&edge[i][0],&edge[i][1],&t);
time[i]=-t;
}
if(bellman_ford())
printf("YES\n");
else
printf("NO\n");
}
return 0;
}