POJ 3259 Wormholes(基于Dijkstra的Bellman-Ford的算法---解决负权边)

题目链接:https://cn.vjudge.net/contest/314508#problem/E

翻译:
在探索他的许多农场时,农夫约翰发现了许多令人惊奇的虫洞。虫洞是非常特殊的,因为它是一条单行道,在你进入虫洞之前把你送到目的地!FJ的每一个农场都包括N(1≤N≤500)个区域,方便编号为1…NM(1≤M≤2500)条路径和W(1≤W≤200)个虫洞
因为FJ是一个狂热的时间旅行迷,他想做以下的事情:从某个领域开始,穿过一些路径和虫洞,在他最初离开之前的一段时间回到起始领域。也许他能认识自己:)。
为了帮助FJ了解这是否可行,他将向您提供其农场F(1≤F≤5)的完整地图。任何路径的运行时间都不会超过10000秒,任何虫洞都不能使FJ及时返回10000秒以上。
输入
第1行:一个整数,F.F农场描述如下。
每个农场的第1行:三个空间分隔的整数:n、m和w
第2行…每个农场的M+1:三个空格分隔的数字(S、E、T),分别表示:S和E之间的双向路径,需要T秒才能穿过。两个字段可以通过多个路径连接。
线M+2…每个农场的M+W+1:分别描述三个空格分隔的数字(S、E、T):从S到E的单向路径,也将旅行者向后移动T秒。
输出
第1行…F:对于每一个农场,如果FJ能够实现他的目标,输出“是”,否则输出“否”(不包括报价)。

在他最初离开之前的一段时间回到起始领域==所给的地图是否含有负权回路

Bellman-Ford的算法核心代码:

for(int k=1; k<n; k++)
    for(int i=1; i<=m; i++)
        if(dsi[v[i]]>dis[u[i]]+w[i])
            dis[v[i]]=dis[u[i]]+w[i];

解释
dis数组的作用与Dijkstra算法中的作用一样。
看能否通过u[i]—>v[i](权值为w[i])这条边,使得一号顶点到v[i]点的距离变短。
对边进行最多n-1轮松弛操作(可以找一个量标记,提高效率)。

为什么进行n-1轮松弛操作
1。在一个含有n个顶点的图中,任意两点之间的最短路径最多包含n-1条边。
2。进行k轮,得到的就是1号顶点“最多经过k条边”到达其余各顶点的最短路径长度。

如何判断负权回路?
进行n-1次松弛操作后,最短路径不会在发生变化了,若仍然成功松弛,必然存在负权回路。

代码:

#include<cstdio>//判断是否具有负权环
#include<cstring>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
struct node{
    int u,v,w;
} e[250005];
int main(){
    int N,M,W; 
    int dis[250005];
    int T;
    scanf("%d",&T);
    while(T--){
        int x=1;
        scanf("%d%d%d",&N,&M,&W);//N个点,M条路径,W个虫洞
        int t1,t2,t3;
        for(int i=0; i<M; i++){//正常的双向道路
            scanf("%d%d%d",&t1,&t2,&t3);
            e[x].u=t1,e[x].v=t2,e[x++].w=t3;
            e[x].u=t2,e[x].v=t1,e[x++].w=t3;
        }
        for(int i=0; i<W; i++){//单向的黑洞
            scanf("%d%d%d",&t1,&t2,&t3);
            e[x].u=t1,e[x].v=t2,e[x++].w=-t3;
        }
        for(int i=1; i<=N; i++)
            dis[i]=INF;
        dis[1]=0;
        int check;
        for(int j=1; j<N; j++){
            check=0;//用来标记在本轮操作中dis是否更新
            for(int i=1; i<x; i++){
                if(dis[e[i].v]>dis[e[i].u]+e[i].w){
                    dis[e[i].v]=dis[e[i].u]+e[i].w;
                    check=1;
                }
            }
            if(check==0)
                break;
        }
        if(check==0)
            printf("NO\n");
        else{
            int flag=0;
            for(int i=0; i<x; i++)//是否仍然可以更新
            {
                if(dis[e[i].v]>dis[e[i].u]+e[i].w)
                {
                    flag=1;
                    break;
                }
            }
            if(flag==1)
                printf("YES\n");
            else
                printf("NO\n");
        }
    }
    return 0;
}

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zaiyang遇见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值