题目链接:http://poj.org/problem?id=3259

题意是问是否能通过虫洞回到过去;

虫洞是一条单向路,不但会把你传送到目的地,而且时间会倒退Ts。

我们把虫洞看成是一条负权路,问题就转化成求一个图中是否存在负权回路;

1.bellman_ford算法

Bellman-Ford算法流程分为三个阶段:

(1)初始化:将除源点外的所有顶点的最短距离估计值 d[v] ←+∞, d[s] ←0;

(2)迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V中的每个顶点的最短距离估计值逐步逼近其最短距离;(运行|v|-1次)

(3)检验负权回路:判断边集E中的每一条边的两个端点是否收敛。如果存在未收敛的顶点,则算法返回false,表明问题无解;否则算法返回true,并且从源点可达的顶点

v的最短距离保存在 d[v]中。

2.spfa算法

我们都知道spfa算法是对bellman算法的优化,那么如何用spfa算法来判断负权回路呢?我们考虑一个节点入队的条件是什么,只有那些在前一遍松弛中改变了距离估计值的点,才可能引起他们的邻接点的距离估计值的改变。因此,用一个先进先出的队列来存放被成功松弛的顶点。同样,我们有这样的定理:“两点间如果有最短路,那么每个结点最多经过一次。也就是说,这条路不超过n-1条边。”(如果一个结点经过了两次,那么我们走了一个圈。如果这个圈的权为正,显然不划算;如果是负圈,那么最短路不存在;如果是零圈,去掉不影响最优值)。也就是说,每个点最多入队n-1次(这里比较难理解,需要仔细体会,n-1只是一种最坏情况,实际中,这样会很大程度上影响程序的效率)。

有了上面的基础,思路就很显然了,加开一个数组记录每个点入队的次数(num),然后,判断当前入队的点的入队次数,如果大于n-1,则说明存在负权回路。

BellmanFord:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <math.h>
#include <queue>
#include <algorithm>
using namespace std;
#define N 5210
#define INF 0xfffffff int cnt, dist[N], Head[N];
int n, m, w; struct Edge
{
int u, v, w, next;
}e[N]; void Add(int u, int v, int w)
{
e[cnt].u = u;
e[cnt].v = v;
e[cnt].w = w;
e[cnt].next = Head[u];
Head[u] = cnt++;
} bool BellmanFord()
{
dist[] = ;
for(int i=; i<n; i++)
{
for(int j=; j<cnt; j++)
{
if(dist[e[j].v] > dist[e[j].u]+e[j].w)
dist[e[j].v] = dist[e[j].u]+e[j].w;
}
}
for(int i=; i<cnt; i++)
{
if(dist[e[i].v] > dist[e[i].u]+e[i].w)
return ;
}
return ;
} int main()
{
int T, a, b, c;
scanf("%d", &T);
while(T--)
{
scanf("%d%d%d", &n, &m, &w); cnt = ;
memset(Head, -, sizeof(Head));
for(int i=; i<=n; i++)
dist[i] = INF; for(int i=; i<=m; i++)
{
scanf("%d%d%d", &a, &b, &c);
Add(a, b, c);
Add(b, a, c);
}
for(int i=; i<=w; i++)
{
scanf("%d%d%d", &a, &b, &c);
Add(a, b, -c);
} if( !BellmanFord() )
printf("YES\n");
else
printf("NO\n");
}
return ;
}

 spfa:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <math.h>
#include <queue>
#include <algorithm>
using namespace std;
#define N 5210
#define INF 0xfffffff int cnt, dist[N], Head[N], num[N], vis[N];
int n, m, w; struct Edge
{
int v, w, next;
}e[N]; void Add(int u, int v, int w)
{
e[cnt].v = v;
e[cnt].w = w;
e[cnt].next = Head[u];
Head[u] = cnt++;
} bool spfa()///spfa模板;
{
memset(vis, , sizeof(vis));
memset(num, , sizeof(num));
queue<int>Q;
vis[] = ;
dist[] = ;
Q.push();
num[]++;
while(Q.size())
{
int p=Q.front();
Q.pop();
vis[p] = ;
for(int i=Head[p]; i!=-; i=e[i].next)
{
int q = e[i].v;
if(dist[q] > dist[p] + e[i].w)
{
dist[q] = dist[p] + e[i].w;
if(!vis[q])
{
vis[q] = ;
Q.push(q);
num[q] ++;
if(num[q]>n)
return true;
}
}
}
}
return false;
} int main()
{
int T, a, b, c;
scanf("%d", &T);
while(T--)
{
scanf("%d%d%d", &n, &m, &w); cnt = ;
memset(Head, -, sizeof(Head));
for(int i=; i<=n; i++)
dist[i] = INF; for(int i=; i<=m; i++)
{
scanf("%d%d%d", &a, &b, &c);
Add(a, b, c);
Add(b, a, c);
}
for(int i=; i<=w; i++)
{
scanf("%d%d%d", &a, &b, &c);
Add(a, b, -c);
} if( spfa() )///存在负环;
printf("YES\n");
else
printf("NO\n");
}
return ;
}

Wormholes---poj3259(最短路 spfa 判断负环 模板)的更多相关文章

  1. POJ 3259 Wormholes【最短路/SPFA判断负环模板】

    农夫约翰在探索他的许多农场,发现了一些惊人的虫洞.虫洞是很奇特的,因为它是一个单向通道,可让你进入虫洞的前达到目的地!他的N(1≤N≤500)个农场被编号为1..N,之间有M(1≤M≤2500)条路径 ...

  2. Wormholes 虫洞 BZOJ 1715 spfa判断负环

    John在他的农场中闲逛时发现了许多虫洞.虫洞可以看作一条十分奇特的有向边,并可以使你返回到过去的一个时刻(相对你进入虫洞之前).John的每个农场有M条小路(无向边)连接着N (从1..N标号)块地 ...

  3. POJ3259 Wormholes(SPFA判断负环)

    Description While exploring his many farms, Farmer John has discovered a number of amazing wormholes ...

  4. POJ 3259 Wormholes ( SPFA判断负环 && 思维 )

    题意 : 给出 N 个点,以及 M 条双向路,每一条路的权值代表你在这条路上到达终点需要那么时间,接下来给出 W 个虫洞,虫洞给出的形式为 A B C 代表能将你从 A 送到 B 点,并且回到 C 个 ...

  5. Wormholes POJ - 3259 spfa判断负环

    //判断负环 dist初始化为正无穷 //正环 负无穷 #include<iostream> #include<cstring> #include<queue> # ...

  6. spfa判断负环

    会了spfa这么长时间竟然不会判断负环,今天刚回.. [例题]poj3259 题目大意:当农场主 John 在开垦他的农场时,他发现了许多奇怪的昆虫洞.这些昆虫洞是单向的,并且可以把你从入口送到出口, ...

  7. UVA 558 SPFA 判断负环

    这个承认自己没看懂题目,一开始以为题意是形成环路之后走一圈不会产生负值就输出,原来就是判断负环,用SPFA很好用,运用队列,在判断负环的时候,用一个数组专门保存某个点的访问次数,超过了N次即可断定有负 ...

  8. spfa 判断负环 (转载)

    当然,对于Spfa判负环,实际上还有优化:就是把判断单个点的入队次数大于n改为:如果总的点入队次数大于所有点两倍 时有负环,或者单个点的入队次数大于sqrt(点数)有负环.这样时间复杂度就降了很多了. ...

  9. Extended Traffic LightOJ - 1074 spfa判断负环

    //判断负环 在负环内的城市输出? #include <iostream> #include <queue> #include <cstdio> #include ...

随机推荐

  1. CentOS系统程序包管理器【rpm、yum】

    将编译好的文件打包成一个或有限的几个文件,可用于实现便捷的安装.卸载.升级.查询,校验等程序管理. centos常用的程序管理器有rpm和yum rpm: redhat package manager ...

  2. 与其他Javascript类库冲突解决方案

    $(document).ready(function() { var $jq = jQuery.noConflict(); $jq('#id').show(); });

  3. WiFi(网络)调试Android手机

    手机需要root 使用adb tcpip命令开启网络调试功能,一旦手机重启,又要重复这些步骤,比较麻烦. 一劳永逸的方法是,使用re管理器(给予root权限)在手机的/system/build.pro ...

  4. js中页面跳转(href)中文参数传输方式

    编码: escape(参数); 解码: unescape(参数);

  5. cesium图形上加载图片

    <!DOCTYPE html> <html> <head> <!-- Use correct character set. --> <meta c ...

  6. 世纪佳缘信息爬取存储到mysql,下载图片到本地,从数据库选取账号对其发送消息更新发信状态

    利用这种方法,可以把所有会员信息存储下来,多线程发信息,10秒钟就可以对几百个会员完成发信了. 首先是筛选信息后爬取账号信息, #-*-coding:utf-8-*- import requests, ...

  7. swift - UILabel的用法

    1.label的声明 class FirstyViewController: UIViewController { var label = UILabel()//初始化 override func v ...

  8. ref 属性使用eslint报错

    react 使用 ref 报错 ,[eslint] Using string literals in ref attributes is deprecated. (react/no-string-re ...

  9. EHcache经典配置

    记录重要的东西和常用的东西. <ehcache> <!-- 指定一个文件目录,当EHCache把数据写到硬盘上时,将把数据写到这个文件目录下 --> <diskStore ...

  10. oracle SUM函数

    select change_type as change_type, sum(points1) as points from (select DECODE(p.change_type, , ' 兑换商 ...