UVA - 558 Wormholes (SPEA算法模板题)
先给出题面:https://vjudge.net/problem/UVA-558
题意描述:给你含n个点以及m条边的图,让你判断在这个图中是否存在负权回路。
首先,我们来介绍什么是SPEA算法
SPFA算法是求解单源最短路径问题的一种算法,由理查德·贝尔曼(Richard Bellman) 和 莱斯特·福特 创立的。有时候这种算法也被称为 Moore-Bellman-Ford 算法,因为 Edward F. Moore 也为这个算法的发展做出了贡献。它的原理是对图进行V-1次松弛操作,得到所有可能的最短路径。其优于迪科斯彻算法的方面是边的权值可以为负数、实现简单,缺点是时间复杂度过高,高达 O(VE)。但算法可以进行若干种优化,提高了效率。
这个算法的思路是:
用数组dis记录每个结点的最短路径估计值,用邻接表或邻接矩阵来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。
具体来说,其核心代码实现思路是:
for(int k = ;k<=n-;k++)
for(int i = ;i<=m;i++)
if(dis[v[i] > dis[u[i] + w[i])
dis[v[i]] = dis[u[i]] + w[i];
看到这段代码是不是有些熟悉,觉得有点像Bellmen-Ford算法,事实上,SPEA就是Bellmen-Ford算法的队列优化。
因此,要想理解SPEA算法,就应该先了解Bellmen-Ford算法,有时间我也会补上一篇关于Bellmen-Ford算法的博客的。
在这里,我就简要的说一下Bellmen-Ford算法的思路
用一个dis数组记录图中各个点到源点(下面将以0为源点讲解)的距离,将dis[0] 初始化为0,其他各点初始化为INF(即无穷大,一般根据题目条件来定),接下来进行n-1轮松弛操作,参考上面的代码,就是判断对于某个点v[i],能否通过源点 0 到 点u[i] ,再从点u[i] 到 点v[i] 这条路径来缩小 源点 0 到 v[i] 的距离。进行完第一轮循环之后,我们得到dis数组事实上就是从0号点出发,只经过一条边,到各个点的最短路径,因为最短路径事实上最多有n-1条边,所以这种松弛会进行n-1操作。最终,我们得到就是从源点0出发,经过给出的m条边,到各个点的最短路。此时的算法复杂度为O(n*m)。
接下来,我们思考来如何优化一下
我们真的需要进行n-1松弛吗?事实上,如果在某一轮松弛中,dis数组没有发生任何更新,那么在这次松弛之后所有的松弛也一定不会改变dis数组,所以后面所有的循环都是没有必要的,因此,我们可以加入一个flag,来判断这一轮松弛是否更新了dis数组,如果没有更新,直接跳出即可。具体代码实现如下:
for(int k = ;k<=n-;k++)
{
bool flag = true;
for(int i = ;i<=m;i++)
if(dis[v[i] > dis[u[i] + w[i])
{
dis[v[i]] = dis[u[i]] + w[i];
flag = false;
}
if(flag)
break;
}
接下来,我们继续来想,什么时候dis[v[i]]的值可能会发生改变,从上面给出的式子来说,因为w[i]是根据输入定的,所以不可改变,所以,只有当dis[u[i]]的值发生改变时,dis[v[i]]的值才可能会发生改变,那么也就是说,只有当与dis值改变的点的点的dis值才有可能改变,有点绕,可能需要思考一段时间。所以我们可以把每次松弛操作中dis值改变的点放在集合中,下次只对与这些点相连的点进行松弛,必将这些点放进集合中,但是重复的点不应该出现在集合中,所以我们要有一个vis数组判断当前这个点是否在集合中。代码实现如下(此处用邻接表存的边,vec[u][i].to表示有一条从u指向它的边,权值为vec[u][i].val):
queue<int>q;
q.push();
vis[] = ;
while(!q.empty())
{
int d = q.front();
q.pop();
vis[d] = ;
for(int i = ;i<vec[d].size();i++)
{
if(dis[vec[d][i].to]>dis[d]+vec[d][i].val)
{
dis[vec[d][i].to]=dis[d]+vec[d][i].val;
if(!vis[vec[d][i].to])
{
vis[vec[d][i].to] = ;
q.push(vec[d][i].to);
} }
}
}
这也就是SPEA算法的真正核心代码。
接下来我们来思考如何判断是否存在负权回路,假设一个图中存在一个负权回路,那这个图中是不存在最短路的,因为每走一次负权回路,源点到某些点的距离一定会改变的。对应上面代码,就会出现死循环,因为有些点的dis值会一直改变,也就是有些点会进入队列很多次,这是一个突破口,那么一个点到底进入队列多少次算是存在负权回路那,思考最开始说的,图的最短路最多有n-1条边,所以,一个点最多应该进入队列n-1次,因此,当一个进入队列的次数大于等于n次时,该图中就存在负权回路。因此我们只要在上面代码中加上这一条件的判断就能判断是否存在负权回路了,最后给出本题的ac代码。
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
const int maxn = +;
const int INF = 0x3f3f3f3f;
struct V
{
int to,val;
V(int a,int b):to(a),val(b){}
};
vector<V>vec[maxn];
int vis[maxn],dis[maxn],num[maxn];
int n,m;
void init()
{
for(int i = ;i<n;i++)
{
vec[i].clear();
vis[i] = ;
num[i] = ;
dis[i] = INF;
}
dis[] = ;
}
bool check()
{
queue<int>q;
q.push();
vis[] = ;
num[]++;
while(!q.empty())
{
int d = q.front(); q.pop();
vis[d] = ;
for(int i = ;i<vec[d].size();i++)
{ //printf("d = %d,%d,%d\n",d,dis[vec[d][i].to],dis[d]+vec[d][i].val);
if(dis[vec[d][i].to]>dis[d]+vec[d][i].val)
{
dis[vec[d][i].to]=dis[d]+vec[d][i].val;
if(!vis[vec[d][i].to])
{
num[vec[d][i].to]++;
vis[vec[d][i].to] = ;
q.push(vec[d][i].to); if(num[vec[d][i].to]>=n)
{
return true;
}
} }
}
}
return false;
}
int main()
{
//freopen("out.txt","w",stdout);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&n,&m);
init();
int a,b,c;
for(int i = ;i<m;i++)
{
scanf("%d %d %d",&a,&b,&c);
vec[a].push_back(V(b,c));
//vec[b].push_back(V(a,c));
}
if(check())
printf("possible\n");
else
printf("not possible\n");
}
return ;
}
本文结束....
UVA - 558 Wormholes (SPEA算法模板题)的更多相关文章
- hdu 1711 KMP算法模板题
题意:给你两个串,问你第二个串是从第一个串的什么位置開始全然匹配的? kmp裸题,复杂度O(n+m). 当一个字符串以0为起始下标时.next[i]能够描写叙述为"不为自身的最大首尾反复子串 ...
- POJ 3041 匈牙利算法模板题
一开始预习是百度的算法 然后学习了一下 然后找到了学长的ppt 又学习了一下.. 发现..居然不一样... 找了模板题试了试..百度的不好用 反正就是wa了..果然还是应当跟着学长混.. 图两边的点分 ...
- uva 558 - Wormholes(Bellman Ford判断负环)
题目链接:558 - Wormholes 题目大意:给出n和m,表示有n个点,然后给出m条边,然后判断给出的有向图中是否存在负环. 解题思路:利用Bellman Ford算法,若进行第n次松弛时,还能 ...
- poj 1274 The Perfect Stall【匈牙利算法模板题】
The Perfect Stall Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 20874 Accepted: 942 ...
- Bellman-Ford算法模板题
POJ 3259 虫洞(Bellman-Ford判断有无负环的问题) 描述: 在探索他的许多农场时,Farmer John发现了许多令人惊叹的虫洞.虫洞是非常奇特的,因为它是一条单向路径,在您进入虫洞 ...
- POJ 1459 Power Network(网络最大流,dinic算法模板题)
题意:给出n,np,nc,m,n为节点数,np为发电站数,nc为用电厂数,m为边的个数. 接下来给出m个数据(u,v)z,表示w(u,v)允许传输的最大电力为z:np个数据(u)z,表示发电 ...
- UVA 10820 欧拉函数模板题
这道题就是一道简单的欧拉函数模板题,需要注意的是,当(1,1)时只有一个,其他的都有一对.应该对欧拉函数做预处理,显然不会超时. #include<iostream> #include&l ...
- UVA 796 Critical Links(模板题)(无向图求桥)
<题目链接> 题目大意: 无向连通图求桥,并将桥按顺序输出. 解题分析: 无向图求桥的模板题,下面用了kuangbin的模板. #include <cstdio> #inclu ...
- HDU - 2255 奔小康赚大钱 KM算法 模板题
HDU - 2255 题意: 分配n所房子给n个家庭,不同家庭对一所房子所需缴纳的钱是不一样的,问你应当怎么分配房子,使得最后收到的钱最多. 思路: KM算法裸题.上模板 #include <i ...
随机推荐
- PyCharm for windows 快捷功能(图片形式讲解)
- C#总结小程序
主要功能 左侧树状图功能 添加,修改,删除 1.添加 点击添加按钮执行点击事件 弹出机窗口之后点击添加 2.删除 点击删除执行下面的单击事件 点击确定即可删除成功. 3.修改
- Quick Sort -- 快速排序算法
//参数说明: // int data[] : 待排序的数据数组 // int m : 下限值 // int n : 上限值 void QuickSort ( int data[] , int m , ...
- python基础第一天 3.27
# #作业1# 猜年龄,可以让用户猜三次!age = 25user_guess = int(input("input your guess")) age = 25count = ...
- 低版本IDE 打开 高版本 IDE 代码时 unit
可以用单元别名 比如Vcl.Forms=Forms 来兼容.
- web分页控件AspNetPager的使用
首先要先引用AspNetPager.dll文件 然后在<html>上面添加下面代码: <%@ Register Assembly="AspNetPager" Na ...
- PHP运算符知识
1.三目运算符: $a =1; echo $a>0 ? '大于0':$a==0 ? '等于0':'小于0'; 貌似应该输出:大于0 其实: 然而,上面语句的实际输出是't',因为三元运算符是从左 ...
- python 全栈开发笔记 1
将自己的姓名用进制表示出来 #自己姓名的进制表示 name=input('请输入你的名字:') for i in name: print(i) # python 3 中是按字符进行循环的 bytes_ ...
- JavaScript 实时 全角转半角
//JavaScript全角字符转半角(参数str为input框输入的内容)var $fullChar2halfChar = function(str) { var result = ''; for ...
- IDEA2017 免费激活方法
选择license server,然后填下面地址,选择一个就好. http://intellij.mandroid.cn/ http://idea.imsxm.com/ http://idea.ite ...