图的一些操作

这次我们来看一下图论的一些有趣的操作与性质

spfa判断负环

首先我们把spfa的代码放过来:

void spfa(){
	memset(dis,0x3f,sizeof(dis));
	memset(vis,0,sizeof(vis));
	memset(cnt,0,sizeof(cnt));
	dis[1]=0; vis[1]=true;
	Q.push(1);
	while(!Q.empty()){
		int x=Q.front();
		Q.pop();
		vis[x]=false;
		for(int i=head[x];i;i=edge[i].next){
			int y=edge[i].to,z=edge[i].w;
			if(dis[y]>dis[x]+z){
				dis[y]=dis[x]+z;
				if(!vis[y]){
					Q.push(y);
					vis[y]=true;
				}
			}
		}
	}
}

然后我们观察,一个数入队的条件是:前一个点当前的dis加上权值w<当前扫描到的边的dis

那么如果出现正环,那么他们每一轮都不会相加,因为显然每加一轮会加上这个环的大小,所以正环不会出现死循环

但是如果出现负环,每一轮过后最短路都会变小,那么我们就可以用spfa来判断负环

方法:计算一下入队的数的个数如果大于等于n,就说明有负环,可以直接跳出了(此处用的是bfs的思路)

bool spfa(){
	memset(dis,0x3f,sizeof(dis));
	memset(vis,0,sizeof(vis));
	memset(cnt,0,sizeof(cnt));
	dis[1]=0; vis[1]=true;
	Q.push(1);
	while(!Q.empty()){
		int x=Q.front();
		Q.pop();
		vis[x]=false;
		for(int i=head[x];i;i=edge[i].next){
			int y=edge[i].to,z=edge[i].w;
			if(dis[y]>dis[x]+z){
				dis[y]=dis[x]+z;
				cnt[y]=cnt[x]+1;  //更新包含边数
				if(cnt[y]>=n) return true;  //判定存在负环
				if(!vis[y]){
					Q.push(y);
					vis[y]=true;
				}
			}
		}
	}
	return false;
}
//上面的更新包含边数可以再说一下,就是说,每当一个环出现,那么肯定要循环跑一串点,所以我们可以记录下每一个点进入多少次,如果一个点进入了n次及以上,就说明有负环了,可以直接跳出

建反边

我们先看一个题:对于一个有向图,从节点1出发,共n个点,每到一个节点立即返回,问最短的总路径是多少
易知我们从节点1到每个点可以用dijkstra跑一遍,那么怎么跑回来呢?每次循环再走一遍?那复杂度不堪设想
所以我们想到可以把边反过来建,这样我们算各个点到节点1的距离可以反过来,重新在建反边的前提下跑一遍dijkstra(1),这就是建反边的思想

posted @ 2021-09-01 10:08  RevolutionBP  阅读(32)  评论(0编辑  收藏  举报