SPFA

SPFA

由最短路算法2中我们可以看到Dijkstra算法并不能帮助我们判断负环(事实上如果用某些模板有负边权就会出错),这时候我们就需要用到SPFA算法了。

实现思路

SPFA算法的思路是队列优化,用一个dis数组计算从源点到每个点的最短路,对于这n个点,m条边,我们从源点出发,搜索从这个点出发的所有边能到达的点,看是否可以更新dis数组,若可以更新,判断该点并有没有在队列中,若不在,则将这个点添加到队列中,并且将这个点进队的次数加一,显然对于一个点,它的最好情况就是每个点都可以更新它所在的dis,因此它的进队次数不应该超过n次,否则证明出现了负环。

模板

void spfa(int i)
{
	q.push(i);
	vis[i] = 1;//要将出发点加入到队列中,所以先标记该点进入队列 
	num[i]++;//出发点已加入一次队列 
	while(!q.empty())
	{	
		int p = q.front();
		q.pop();vis[p] = 0;//取出队首元素 
		int t = find[p];
		while(t != -1)//依次检查从该点出发的各条边,更新最短路径 
		{
			if(dis[p] + map[t].pow< dis[map[t].to])
			{
				dis[map[t].to] = dis[p] + map[t].pow;
				if(vis[map[t].to] == 0)//只有不在队列中的点才能加入 
				{
					q.push(map[t].to);
					vis[map[t].to] = 1;
					num[map[t].to]++;
					if(num[map[t].to] > c)//如果点出现的次数已经超过所有点的总个数,证明存在负环 
					{	
						printf("orz\n");
						return 0;
					}
				}
			}
			t = map[t].next;
		}
	}
} 

例题

例题:赚钱
zzy现在决定环游中国,顺便赚点钱。zzy在一个城市最多只能赚D元,然后他可以选择退休也就是停止赚钱,或者去其它城市工作。当然,他可以在别处工作一阵子后又回到原来的城市再赚D元。这样的往返次数是没有任何限制的。

城市间有P条单向路径连接,共有C座城市,编号从1到C。路径i从城市Ai到城市Bi,在路径行走上不用任何花费。

zzy还可以乘飞机从某个城市飞到另一个城市。共有F条单向的航线,第i条航线是从城市Ji飞到另一座城市Ki,费用是Ti元。假如zzy身上没有现钱,他可以用以后赚的钱来付机票钱。

zzy可以从任何一个城市出发开始赚钱,并且选择在任何时候、任何城市退休。现在zzy想要知道,如果在工作时间上不做限制,那么zzy共可以赚多少钱呢?如果赚的钱也不会出现限制,那么就输出orz。

输入
100 3 5 2
1 5
2 3
1 4
5 2 150
2 5 120
输出
250
解析:
这道题表面上是要求边权最大的路径,但为了使用最短路,我们可以将赚的钱设为负值,花费的钱作为正值,这样就变成了一道SPFA的模板题。

#include
#include
#include
#define INF 0x3f3f3f3f
using namespace std;
struct node
{
	int to,pow,next;
}map[100005];
int find[100005];
int cnt;
int d,p,c,f;
void build(int from,int to,int cost)
{
	map[++cnt].to = to;
	map[cnt].pow = cost -d;
	map[cnt].next = find[from];
	find[from] = cnt;
}
void spfa(int i);
int vis[100005];
int num[100005];
int dis[100005];
int main()
{
	memset(find,-1,sizeof(find));
	queue<int>q;
	scanf("%d%d%d%d",&d,&p,&c,&f);
	int x,y;
	for(int i = 0;i<p;i++)
	{
		scanf("%d%d",&x,&y);
		build(x,y,0);
	}
	int cost;
	for(int i = 0;i <f;i++)
	{
		scanf("%d%d%d",&x,&y,&cost);
		build(x,y,cost);
	}
	int min = INF;
	for(int i = 1;i <= c;i++)
	{
		memset(vis,0,sizeof(vis));
		memset(num,0,sizeof(num));
		memset(dis,0x3f,sizeof(dis));
		dis[i] = -d;
		vis[i] = 1;
		num[i]++;
		q.push(i);
		while(!q.empty())
		{	
			int p = q.front();
			q.pop();vis[p] = 0;
			int t = find[p];
			while(t != -1)
			{
				if(dis[p] + map[t].pow< dis[map[t].to])
				{
					dis[map[t].to] = dis[p] + map[t].pow;
					if(vis[map[t].to] == 0)
					{
						q.push(map[t].to);
						vis[map[t].to] = 1;
						num[map[t].to]++;
						if(num[map[t].to] > c)
						{	
							printf("orz\n");
							return 0;
						}
					}
				}
				t = map[t].next;
			}
		}
		for(int k = 1;k <=c;k++)
		if(min > dis[k])
		min = dis[k];
	}
	printf("%d\n",-min);
}

你可能感兴趣的:(算法学习)