牛客OI周赛5-提高组 wpy的请求 (spfa变形求负权通路)

wpy的请求 spfa变形

题目大意:给一个n个点,m条边的有向图,可能有负权边,现在要把负边权都变成非负,
并且使得原图中的任意两点u,v最短路经过路径不变并且使得原图中的任意两点u,v最短路经过路径不变

解题思路:解法就是建立一个超级原点(也就是0节点),与所有点相连,且边权值为0,然后跑一遍spfa(),然后边权答案就是 dis[u]-dis[v]+w。

这是因为spfa的松弛操作的条件是 dis[v]>dis[u]+w 所以松弛后 dis[u]-dis[v]+w>=0 是一定成立的,
所以边权都为非负了。
在这里插入图片描述

Code:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long int ll;
const int maxn=1e6+10;

ll dis[maxn];
bool vis[maxn];

struct edge {
	int u,v,w,nx;
}e[maxn];
int head[maxn],cnt,n,m;

void add(int u,int v,int w){
	e[++cnt]={u,v,w,head[u]};
	head[u]=cnt;
}

void spfa(){
	for(int i=0;i<=n;i++) dis[i]=inf,vis[i]=0;
	
	queue<int> q;
	dis[0]=0; q.push(0);
	while(!q.empty()){
		int u=q.front(); q.pop();
		vis[u]=0;
		for(int i=head[u];i;i=e[i].nx){
			int v=e[i].v, w=e[i].w;
			if(dis[v]>dis[u]+w) dis[v]=dis[u]+w;
			if(!vis[v]) q.push(v),vis[v]=1;
		}
	}
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=m;i++){
    	int u,v,w;
    	cin>>u>>v>>w;
    	add(u,v,w);
	}
	for(int i=1;i<=n;i++) add(0,i,0);
	
	spfa();
	for(int i=1;i<=m;i++){
		cout<<e[i].u<<" "<<e[i].v<<" "<<dis[e[i].u]-dis[e[i].v]+e[i].w<<"\n";
	}
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43872264/article/details/107682624