在做差分约束时,碰到了SPFA算法,它是Bellman_ford的改进版。。。
觉得Bellman_ford写着很简单,相当简单,无非分三步,第一步:初始化,这一步往往把dis[i] = MAX;而把源点s的dis[s] = 0;
第二步:迭代求解,反复对边集E中的每条边进行松弛操作,使得顶点集V中的每个顶点v的最短距离估计值逐步逼近其最短距离。一般是两重循环,for(int i = 1; i < n; i++) for(int j = 1; j <= edgenum; j++)
n为节点个数,第一重循环是除了源点s外的n-1个,第二重中,edgenum为边的个数,它的确定一般是在输入处理时
循环里边就是那个三角不等式了,if(dis[v] > dis[u] + w) .................再么,加个flag标志位,当一次j 从1到edgenum,if条件都未成立,则可以跳出循环。
第三步:检验是否有负环回路,就是看经过第二步后,是否还能松弛。。。
参考我博客另一篇:http://blog.csdn.net/bill_ming/article/details/7628435
很好的参考:http://www.wutianqi.com/?p=2285
{
if(dis[v] > dis[u] + w)
return false;
}
看图理解:
可以看出,dijkstra不能求带负权值的最短路,而Bellman_ford不仅可以求带负权值的最短路,而且还可以判断是否有负环回路,但是。。。。。。效率很低啊。。。O(顶点数*边数)
原因在于,(不知道表述的对不?)更新每个节点时,都要更新源点到每条边的长度,所以源点到每个顶点的最短长度只有在最后一松弛才能确定,也因此才能计算带有负权值的情况,但其实,每次松弛,有的是没改变的,因此产生了很多冗余计算,所以才有了改进版---SPFA
SPFA对Bellman_ford的优化关键是:只有那些在前一遍松弛中改变距离估计值的点,才可能引起他们邻接点的估计值改变,因此,SPFA用一个队列存放被成功松弛的顶点
大致流程:维护一个队列,初始时将源加入队列,每次取出一个元素,并对所有与他相邻的点进行松弛,若某个相邻的点松弛成功,则入队。直到队列为空,结束。
有一个问题:一个点如果被改变多次,也就是一个点改进过其它的点之后,过了一段时间可能本
身被改进,于是再次用来改进其它的点,这样反复迭代下去。所以我们可以设置一个bool vis[ ],来记录该节点是否入队,我们每次仅将满足条件的未入队的顶点加入队列。。。
但还有一个问题:如果有负环回路,怎么办?那就会反复运行,无穷减小,队列不会为空了
这是怎么办?思考Bellman-Ford算法,它是如何结束的?显然,最朴素的Bellman-Ford算法不管循环过程中发生了什么,一概要循环|V|-1遍才肯结束。凭直觉我们可以感到,SPFA算法“更聪明一些”,就是说我们可以猜测,假如在SPFA中,一个点进入队列——或者说一个点被处理——超过了|V|次,那么就可以断定图中存在负权回路了。
还有一个问题:如果我们要输出最短路径本身怎么办。。。
我们可以用一个path[ ]数组,path[i]表示从S到i的最短路径中,i节点之前的编号。我们再借助节点u对v进行松弛时,标记下path[v] = u;记录就完成了。。。输出时的问题:因为我们记录的是前面的点是什么,输出却要从后边。
void PrintPath(int k)
{
if( path[k] )
PrintPath( path[k] );
cout << k << " ";
}
题目链接:http://poj.org/problem?id=1201
觉得,差分约束,最重要的是找到约束条件,也就是那些不等式,包括给出的,隐含的。。。
然后经过输入时处理后,用Bellman_ford,SPFA,不过要灵活运用,不能死板模板。。。做题还是太少了。。。
题意:给了我们一些区间,然后告诉每个区间中至少需要取Ci个数。求出满足n个条件的集合Z的最少的元素个数。
不等式:
s[b+1]-s[a] >= c; 已给
1>=s[i+1]-s[i]>=0 隐含
我邻接表没怎么用过,都是用邻接矩阵。。。。多做题啊。。
如果发现有错,或者与您想法不符,求留言探讨。。。
//1201 Accepted 3392K 1188MS C++ 1916B
#include <iostream>
#include <cstdio>
#include <string>
#include <queue>
#include <cstring>
using namespace std;
const int M = 151010;
const int INF = -9999999;
struct Edge //邻接表
{
int v;
int w;
int next; //
} edge[M];
int dis[M];
int head[M]; //保存边的头结点编号
bool visit[M];
int num,n,maxx,minn;
int number ;
queue<int> p;
int SPFA()
{
int e;
for(int i = minn; i <= maxx; i++)
dis[i] = INF;
dis[minn] = 0;
p.push(minn);//源点入队
visit[minn] = true; //标记在队中
while( !p.empty() )
{
e = p.front();
p.pop();
visit[e] = false; //标记不在队中
for(int i = head[e]; i != -1; i = edge[i].next)
{
if (dis[edge[i].v] < dis[e] + edge[i].w) //找最长路,三角不等式
{
dis[edge[i].v] = dis[e] + edge[i].w;
if( !visit[edge[i].v] )
{
visit[edge[i].v] = true;
p.push(edge[i].v);
}
}
}
}
return dis[maxx];
}
int main()
{
int a,b,c;
num = 0;
number = 1;
maxx = -9999;
minn = 9999;
memset(dis,0,sizeof(dis));
memset(head,-1,sizeof(head)); //不要写0啊啊啊啊
memset(visit,false,sizeof(visit));
cin >> n;
for(int i = 1; i <= n; i++)
{
cin >> a >> b >> c;
edge[num].next = head[a];
edge[num].v = b + 1;
edge[num].w = c;
head[a] = num;
num ++;
if(maxx < b+1)
maxx = b+1;
if(minn > a)
minn = a;
}
for(int i = minn; i < maxx; i++)
{
edge[num].next = head[i];
edge[num].v = i+1;
edge[num].w = 0;
head[i] = num;
num++;
edge[num].next = head[i+1];
edge[num].v = i;
edge[num].w = -1;
head[i+1] = num;
num++;
}
int ans = SPFA();
cout << ans <<endl;
return 0;
}