题单:最短路
目录
P3003 [USACO10DEC]Apple Delivery S
P4779【模板】单源最短路径(标准版)
题意:
给定一个 n 个点,m 条有向边的带非负权图,请你计算从 s出发,到每个点的距离。数据保证你能从 s 出发到任意点。(模板)
题解:
有向边,没有负边权,可以用Dijkstra
代码:
#include <iostream>
#include <fstream>
#include <set>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <utility>
#include <map>
#include <string.h>
#include <cmath>
#include <queue>
#include <stack>
#include <string>
#include <bitset>
#include <numeric>
#define ll long long
const int inf = (1<<31)-1;
using namespace std;
inline ll read() {ll x = 0, z = 1;char c = getchar();while (!isdigit(c)) {if (c == '-')z = -1;c = getchar();}while (isdigit(c)) {x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return z * x;}
inline void writ(ll x){if(x<0) {putchar('-');x=(~x)+1;}if(x>9)writ(x/10);putchar(x-x/10*10+48);}
const int N=100010;
vector<int> dijkstra(int n,int s,vector<vector<pair<int,int>>>&to){
vector<int>dist(n+1, inf);
vector<int>st(n+1,0);
// 定义小根堆,用于存储未确定最短路的节点
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
// 将源节点s加入小根堆
pq.push(make_pair(0, s));
dist[s]=0;
while (!pq.empty()) {
// 取出堆顶节点u
int u = pq.top().second;
pq.pop();
if(st[u])continue;
st[u]=1;
// 遍历u的邻接节点v
for (auto& e : to[u]) {
int v = e.first;
int w = e.second;
// 如果u到v的距离更短,则更新dist[v]和prev[v]的值,并将v加入小根堆
if (dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
pq.push(make_pair(dist[v], v));
}
}
}
return dist;
}
int main(int argc, char *argv[])
{
//ios::sync_with_stdio(false);
//cin.tie(0);
int n,m,s;
cin>>n>>m>>s;
vector<vector<pair<int,int>>>to(n+1);
for (int i = 0; i < m; i++)
{
int x,y,w;
cin>>x>>y>>w;
to[x].push_back({y,w});
}
vector<int>ans=dijkstra(n,s,to);
for (int i = 1; i <= n; i++)
{
cout<<ans[i]<<" \n"[i==n];
}
return 0;
}
P3003 [USACO10DEC]Apple Delivery S
题意:
无向图,无负环,从起点走过两点的最短距离(从起点开始,连通3个点的最短路径)
题解:
不管怎么走,都要有两条路连通这3个点,连通2个点的最优情况就是他们的最短路,所以我们要选择两条最短路连通这三个点,从起点开始,另外两点必须连接,情况少,我们直接判断。
代码:
#include <iostream>
#include <fstream>
#include <set>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <utility>
#include <map>
#include <string.h>
#include <cmath>
#include <queue>
#include <stack>
#include <string>
#include <bitset>
#include <numeric>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
inline ll read() {ll x = 0, z = 1;char c = getchar();while (!isdigit(c)) {if (c == '-')z = -1;c = getchar();}while (isdigit(c)) {x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return z * x;}
inline void writ(ll x){if(x<0) {putchar('-');x=(~x)+1;}if(x>9)writ(x/10);putchar(x-x/10*10+48);}
const int N=100010;
vector<int> dijkstra(int n,int s,vector<vector<pair<int,int>>>&to){
vector<int>dist(n+1, inf);
vector<int>st(n+1,0);
// 定义小根堆,用于存储未确定最短路的节点
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
// 将源节点s加入小根堆
pq.push(make_pair(0, s));
dist[s]=0;
while (!pq.empty()) {
// 取出堆顶节点u
int u = pq.top().second;
pq.pop();
if(st[u])continue;
st[u]=1;
// 遍历u的邻接节点v
for (auto& e : to[u]) {
int v = e.first;
int w = e.second;
// 如果u到v的距离更短,则更新dist[v]和prev[v]的值,并将v加入小根堆
if (dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
pq.push(make_pair(dist[v], v));
}
}
}
return dist;
}
int main(int argc, char *argv[])
{
//ios::sync_with_stdio(false);
//cin.tie(0);
int n,m,s,a,b;
cin>>m>>n>>s>>a>>b;
vector<vector<pair<int,int>>>to(n+1);
for (int i = 0; i < m; i++)
{
int x,y,w;
cin>>x>>y>>w;
to[x].push_back({y,w});
to[y].push_back({x,w});
}
vector<int>ans1=dijkstra(n,s,to);
vector<int>ans2=dijkstra(n,a,to);
cout<<min(ans1[a],ans1[b])+ans2[b];
return 0;
}
P1576 最小花费
题意:
在 n 个人中,某些人的银行账号之间可以互相转账。这些人之间转账的手续费各不相同。给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问 A 最少需要多少钱使得转账后 B 收到 100 元。
题解:
边权是扣除z%的手续费,就是乘(100-z)/100,是求路径乘积最大值,边权都是小于1的,越少边越好
满足最优子结构(如果是a->c的最优路径,则
一定是b->c的最优路径),直接用dijkstra做
代码:
#include <iostream>
#include <fstream>
#include <set>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <utility>
#include <map>
#include <string.h>
#include <cmath>
#include <queue>
#include <stack>
#include <string>
#include <bitset>
#include <numeric>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
inline ll read() {ll x = 0, z = 1;char c = getchar();while (!isdigit(c)) {if (c == '-')z = -1;c = getchar();}while (isdigit(c)) {x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return z * x;}
inline void writ(ll x){if(x<0) {putchar('-');x=(~x)+1;}if(x>9)writ(x/10);putchar(x-x/10*10+48);}
const int N=100010;
vector<double> dijkstra(int n,int s,vector<vector<pair<int,int>>>&to){
vector<double>dist(n+1, 0);
vector<int>st(n+1,0);
// 定义小根堆,用于存储未确定最短路的节点
priority_queue<pair<double, int>, vector<pair<double, int>>, less<pair<double, int>>> pq;
// 将源节点s加入小根堆
pq.push(make_pair(1, s));
dist[s]=1;
while (!pq.empty()) {
// 取出堆顶节点u
int u = pq.top().second;
pq.pop();
//cout<<u<<' '<<to[u].size()<<endl;
if(st[u])continue;
st[u]=1;
// 遍历u的邻接节点v
for (auto& e : to[u]) {
int v = e.first;
double w = (double)(100-e.second)/100;
// 如果u到v的距离更短,则更新dist[v]和prev[v]的值,并将v加入小根堆
//cout<<u<<' '<<dist[u]*w<<' '<<dist[v]<<' '<<v<<endl;
if (dist[u]*w > dist[v]) {
dist[v] = dist[u]*w;
pq.push(make_pair(dist[v], v));
}
}
}
return dist;
}
int main(int argc, char *argv[])
{
//ios::sync_with_stdio(false);
//cin.tie(0);
int n,m,s;
cin>>n>>m;
vector<vector<pair<int,int>>>to(n+1);
for (int i = 0; i < m; i++)
{
int x,y,w;
cin>>x>>y>>w;
to[x].push_back({y,w});
to[y].push_back({x,w});
}
int ss;
cin>>s>>ss;
vector<double>ans=dijkstra(n,s,to);
printf("%.8lf\n",100/ans[ss]);
return 0;
}
P1690 贪婪的Copy
题意:
在图上从起点开始,要经过p个点(可能包含起点、终点),再到终点的最短距离。n<=100,p<=10
题解:
和上面第二题一样,一条路上经过的相邻两点的最优情况就是两点的最短距离,我们用他们的最短距离来代替这p个点的距离,再求经过p个点的最短距离。
因为要找到经过所有点的路径,不满足最优子结构,是NP问题,只能硬搜,复杂度p!(所以起点、终点的情况要处理好,不然复杂度就从10!到11!或者12!了)。
所以floyd求所有边的距离,再在p个点和1,n中硬搜。
代码:
#include <iostream>
#include <fstream>
#include <set>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <utility>
#include <map>
#include <string.h>
#include <cmath>
#include <queue>
#include <stack>
#include <string>
#include <bitset>
#include <numeric>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
inline ll read() {ll x = 0, z = 1;char c = getchar();while (!isdigit(c)) {if (c == '-')z = -1;c = getchar();}while (isdigit(c)) {x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return z * x;}
inline void writ(ll x){if(x<0) {putchar('-');x=(~x)+1;}if(x>9)writ(x/10);putchar(x-x/10*10+48);}
const int N=100010;
void floyd(int n,vector<vector<int>>&dp){
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
for (int k = 0; k < n; k++)
{
dp[j][k]=min(dp[j][k],dp[j][i]+dp[i][k]);
}
}
}
}
vector<vector<int>>dp;
set<int>p;
vector<int>st;
int n;
int ans=inf;
void dfs(int pos,int val,int num){
if(num==p.size()){
ans=min(ans,val+dp[pos][n-1]);
return;
}
else if(pos==n-1)
{
return;
}
for (auto i:p)
{
if(st[i]){
st[i]=0;
dfs(i,val+dp[pos][i],num+1);
st[i]=1;
}
}
}
int main(int argc, char *argv[])
{
//ios::sync_with_stdio(false);
//cin.tie(0);
cin>>n;
dp.assign(n,vector<int>(n));
st.assign(n,1);
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
dp[i][j]=read();
}
}
floyd(n,dp);
int m;
cin>>m;
int flag=0;
for (int i = 0; i < m; i++)
{
int x=read()-1;
p.insert(x);
}
p.insert(0);
st[0]=0;
dfs(0,0,p.find(0)!=p.end());
cout<<ans<<endl;
return 0;
}
P1073 [NOIP2009 提高组] 最优贸易
题意:
给定一个有向图,其中一些边是双向的,每个节点都有一个权值。你需要从节点 1 出发,最终到达节点 n,可以重复经过任何节点。你可以选择在某个节点买入一个单位的商品,并在之后经过的另一个节点卖出这个单位的商品,用赚取的差价作为收益。你最多只能进行一次买卖。请计算你能获得的最大收益。
题解:
如果有环(包括双边),上面的权值可以随便选,可以用tarjan缩点成一个有向无环图,拓扑排序后dp,dp存到这个点可选的最大最小值。
但是tarjan忘了怎么写。
可以用分层图(每一层都是原图,自己建边连接每一层):用点的权值做层与层之间点i_1->i_2->i_3的权值,选择了一个点就进入了下一层,同一层的路径权值都是0,这些层就构成了路径问题(没有负环,一层上一层,最长、最短路径都有最优子结构,可以SPFA)(分层图第x层的i点是i+x*n,不是直接i*x,笨蛋)。
第一层从s开始选一条边(买入,负权值)进入第二层的对应的点,再选一个点卖出进入第三层,在第三层找到终点n
代码:
#include <iostream>
#include <fstream>
#include <set>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <utility>
#include <map>
#include <string.h>
#include <cmath>
#include <queue>
#include <stack>
#include <string>
#include <bitset>
#include <numeric>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
inline ll read() {ll x = 0, z = 1;char c = getchar();while (!isdigit(c)) {if (c == '-')z = -1;c = getchar();}while (isdigit(c)) {x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return z * x;}
inline void writ(ll x){if(x<0) {putchar('-');x=(~x)+1;}if(x>9)writ(x/10);putchar(x-x/10*10+48);}
const int N=100010;
vector<int>val;
vector<int> SPFA(vector<vector<pair<int,int>>>&to,int s,int n){
vector<int>dist(n,-inf);
vector<bool>vis(n);//是否在队列中
vis[s] = true;
dist[s]=0;
queue<int>q;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
vis[u] = false;
for (auto e : to[u]) {
int v = e.first, w = e.second;
if (dist[v] < dist[u] + w){
dist[v] = dist[u] + w;
if (!vis[v]) {
vis[v] = true;
q.push(v);
}
}
}
}
return dist;
}
int main(int argc, char *argv[])
{
//ios::sync_with_stdio(false);
//cin.tie(0);
int n=read(),m=read();
val.assign(n+1,0);
for (int i = 1; i <= n; i++)
{
val[i]=read();
}
vector<vector<pair<int,int>>>to(3*n+1);
for (int i = 0; i < m; i++)
{
int x=read(),y=read(),k=read();
to[x].push_back({y,0});
to[x+n].push_back({y+n,0});
to[x+n+n].push_back({y+n+n,0});
if(k==2){
to[y].push_back({x,0});
to[y+n].push_back({x+n,0});
to[y+n+n].push_back({x+n+n,0});
}
}
for (int i = 1; i <= n; i++)
{
to[i].push_back({i+n,-val[i]});
to[i+n].push_back({i+n+n,val[i]});
}
vector<int>ans=SPFA(to,1,3*n+1);
cout<<ans[3*n];
return 0;
}
P1608 路径统计
题意:
一个有向图,其中有 N 个节点和 E 条边。每条边从节点 I 到节点 J,花费为 C。你需要从节点 1 出发,到达节点 N,计算最少的花费以及花费最少的路径的总数。
坑:两个不同的最短路方案要求:路径长度相同(均为最短路长度)且最短路经过的点的编号序列不同,而数据提供的边信息可能会重复
题解:
一种常用的方法是使用动态规划。首先,使用 Dijkstra 算法或 Bellman-Ford 算法计算出两个节点之间的最短距离。然后,使用动态规划来计算最短路径的数量。设 dp[i] 表示从起点到节点 i 的最短路径数量,那么对于每条边 (u, v),如果 dis[u] + w(u, v) == dis[v],则 dp[v] += dp[u]。
我们要确定dp顺序,有环我们不能拓扑排序,SPFA就不行了,一个点,可能会更新好几次,边 (u, v)中,u的来源我们不能确定,如果是没有加过的边我们要dp[v] += dp[u],如果是已经加过的边,他更新了,我们不能知道它更新了多少,如图:
dijkstra每点就更新一次就可以。
代码:
#include <iostream>
#include <fstream>
#include <set>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <utility>
#include <map>
#include <string.h>
#include <cmath>
#include <queue>
#include <stack>
#include <string>
#include <bitset>
#include <numeric>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
inline ll read() {ll x = 0, z = 1;char c = getchar();while (!isdigit(c)) {if (c == '-')z = -1;c = getchar();}while (isdigit(c)) {x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return z * x;}
inline void writ(ll x){if(x<0) {putchar('-');x=(~x)+1;}if(x>9)writ(x/10);putchar(x-x/10*10+48);}
const int N=100010;
vector<int>dp;
vector<int> Dijkstra(vector<vector<pair<int,int>>>&to,int s,int n){
// 初始化距离数组和前驱数组
vector<int>st(n,0);
vector<int>dist(n, inf);
dp.assign(n,0);
dp[s]=1;
dist[s] = 0;
// 定义小根堆,用于存储未确定最短路的节点
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
// 将源节点s加入小根堆
pq.push(make_pair(0, s));
while (!pq.empty()) {
// 取出堆顶节点u
int u = pq.top().second;
pq.pop();
if(st[u])continue;
st[u]=1;
// 遍历u的邻接节点v
for (auto& e : to[u]) {
int v = e.first;
int w = e.second;
// 如果u到v的距离更短,则更新dist[v]的值,并将v加入小根堆
if(dist[u]+w==dist[v])dp[v]+=dp[u];
if (dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
dp[v]=dp[u];
pq.push(make_pair(dist[v], v));
}
}
}
return dist;
}
int main(int argc, char *argv[])
{
//ios::sync_with_stdio(false);
//cin.tie(0);
int n=read(),m=read();
vector<vector<pair<int,int>>>to(n+1);
vector<vector<int>>vis(n+1,vector<int>(n+1,-1));//有重边,去重
for (int i = 0; i < m; i++)
{
int x=read(),y=read(),w=read();
if(~vis[x][y]){if(w<to[x][vis[x][y]].second)to[x][vis[x][y]].second=w;continue;}//权值可能要更新
vis[x][y]=to[x].size();
to[x].push_back({y,w});
}
vector<int>ans=Dijkstra(to,1,n+1);
if(dp[n])cout<<ans[n]<<' '<<dp[n];
else cout<<"No answer";
return 0;
}
P1613 跑路
题意:
一个有向图,50个点,边权为距离1,每秒可以走2的任意次幂,求到终点的最短时间(二进制中 1 的个数最少的路径)
题解:
对于求二进制中 1 的个数最少的路径,没有最优子结构(应该与它不能线性积累有关)。实际上,这个所求的目标是间接来的,直接上是时间的最短路,我们处理一下权值就行。
代码中先处理1秒能到走的所有路径,通过dp处理,并建立了权值为时间的路径to,它有最优子结构,再用floyd求解。
代码:
#include <iostream>
#include <fstream>
#include <set>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <utility>
#include <map>
#include <string.h>
#include <cmath>
#include <queue>
#include <stack>
#include <string>
#include <bitset>
#include <numeric>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
inline ll read() {ll x = 0, z = 1;char c = getchar();while (!isdigit(c)) {if (c == '-')z = -1;c = getchar();}while (isdigit(c)) {x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return z * x;}
inline void writ(ll x){if(x<0) {putchar('-');x=(~x)+1;}if(x>9)writ(x/10);putchar(x-x/10*10+48);}
const int N=100010;
void init(vector<vector<vector<bool>>>&dp,int n,vector<vector<int>>&to){
for (int k = 1; k < 32; k++)
for (int z = 0; z < n; z++)
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
dp[i][j][k]=dp[i][j][k]|(dp[i][z][k-1]&dp[z][j][k-1]);
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
for (int k = 0; k < 32; k++)
if(dp[i][j][k])to[i][j]=1;
}
void floyd(vector<vector<int>>&to,int n){
for (int k = 0; k < n; k++)
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
to[i][j]=min(to[i][j],to[i][k]+to[k][j]);
}
int main(int argc, char *argv[])
{
//ios::sync_with_stdio(false);
//cin.tie(0);
int n=read()+1,m=read();
vector<vector<vector<bool>>>dp(n,vector<vector<bool>>(n,vector<bool>(32,0)));
vector<vector<int>>to(n,vector<int>(n,inf));
for (int i = 0; i < m; i++)
{
int x=read(),y=read();
dp[x][y][0]=1;
}
init(dp,n,to);
floyd(to,n);
cout<<to[1][n-1];
return 0;
}
P5905 【模板】Johnson 全源最短路
题意:
给定一个包含 n 个结点和 m条带权边的有向图,求所有点对间的最短路径长度
-
边权可能为负,且图中可能存在重边和自环;
-
部分数据卡 n 轮 SPFA 算法。
若图中存在负环,输出仅一行 −1。
若图中不存在负环:
输出 n 行:令 dis_{i,j} 为从 i 到 j 的最短路,在第 i行输出
注意这个结果可能超过 int 存储范围。
如果不存在从 i 到 j 的路径,则 dis_{i,j}=10^9;如果 i=j,则 dis_{i,j}=0。
1≤n≤3×10^3, 1≤m≤6×10^3, 1≤u,v≤n, −3×10^5≤w≤3×10^5
题解:
有负边,floyd的O(n^3)的复杂度太大,n次SPFA一般可以,但题目卡数据。
Dijkstra是肯定够的,我们怎么能处理边权都为正呢?这就是johnson的处理,"re-right"。
设原来的所有 i 到 j 的路径的集合为,那我们需要求的就是它的最小值。
我们先想一下,都加一个超级大的权值可以吗,然后记录最短路的边数,结果减去这个权值乘边数?
结果不行,我们想一下我们加了边权x后,我们求的是的最小值,l(length)表示对应路径的边数。也就是说,我们加入了新的参数影响了求的最短路径(就是路径)。
johnson的操作是这样的:
先求一遍最短路f[ ],那么,那么
那么我们把边权换成了,
dist′=weight(u,v_1)+f[u]−f[v_1]+weight(v_1,v_2)+f[v_1]−f[v_2]+...+weight(v_n,v)+f[v_n]−f[v]=weight(u,v_1)+weight(v_1,v_2)+...+weight(v_n,v)+f[u]−f[v]
=dist+f[u]−f[v]
f[u]-f[v]是只与起点、终点有关的常数,我们求的是(W_{i,j}+f[i]-f[j])的最小值,就是原来的路径。
为了让图连通,我们加入一个超级源点求f[],因为有负值,我们用SPFA,然后在原图上求n次Dijkstra,复杂度O(nm+n^2logm)。
虽然复杂度可能接近n次SPFA,但谁让SPFA不稳定可以被卡掉呢。
代码:
#include <iostream>
#include <fstream>
#include <set>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <utility>
#include <map>
#include <string.h>
#include <cmath>
#include <queue>
#include <stack>
#include <string>
#include <bitset>
#include <numeric>
#define ll long long
#define inf 1e9
using namespace std;
inline ll read() {ll x = 0, z = 1;char c = getchar();while (!isdigit(c)) {if (c == '-')z = -1;c = getchar();}while (isdigit(c)) {x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return z * x;}
inline void writ(ll x){if(x<0) {putchar('-');x=(~x)+1;}if(x>9)writ(x/10);putchar(x-x/10*10+48);}
const int N=100010;
bool SPFA(vector<vector<pair<int,int>>>&to,vector<int>&f,int s,int n){
vector<bool>vis(n);//是否在队列里;
queue<int>q;
vector<int>cnt(n,0);
q.push(s);
vis[s]=1;
f[s]=0;
while(!q.empty()){
int v=q.front();
q.pop();
vis[v]=0;
if(cnt[v]>=n)return false;
int u,w;
for (auto e:to[v])
{
int u=e.first;
int w=e.second;
if(f[v]+w<f[u]){
f[u]=f[v]+w;
if(!vis[u]){q.push(u);vis[u]=1;cnt[u]++;}
}
}
}
return true;
}
vector<int> dijkstra(vector<vector<pair<int,int>>>&to,int s,int n){
vector<int>dist(n,inf);
vector<bool>vis(n,0);
dist[s]=0;
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>>pq;
pq.push({0,s});
while(!pq.empty()){
int v=pq.top().second;
pq.pop();
if(vis[v])continue;
vis[v]=1;
for (auto& e:to[v])
{
int u=e.first,w=e.second;
if(dist[v]+w<dist[u]){
dist[u]=dist[v]+w;
pq.push({dist[u],u});
}
}
}
return dist;
}
int main(int argc, char *argv[])
{
//ios::sync_with_stdio(false);
//cin.tie(0);
int n=read()+1,m=read();
vector<vector<pair<int,int>>>to(n);
vector<int>f(n,inf);
vector<vector<int>>d(n,vector<int>(n));
for (int i = 0; i < m; i++)
{
int x=read(),y=read(),w=read();
to[x].push_back({y,w});
}
//建立超级源点
for (int i = 0; i < n; i++)
{
to[0].push_back({i,0});
}
//计算f[]
if(!SPFA(to,f,0,n)){
cout<<-1<<endl;
return 0;
}
//去掉超级源点
to[0].clear();
//更新路径权值
for (int i = 0; i < n; i++)
for (auto&k:to[i])
k.second+=f[i]-f[k.first];
//求最短路,并修正
vector<vector<int>>ans(n);
for (int i = 1; i < n; i++)
{
ans[i]=dijkstra(to,i,n);
for (int j = 1; j < n; j++)
if(ans[i][j]!=inf)ans[i][j]-=f[i]-f[j];//如果不存在边就不修正;
// for (int j = 0; j < n; j++)
// {
// cout<<ans[i][j]<<" \n"[j==n-1];
// }
}
for (int i = 1; i < n; i++)
{
ll sum=0;
for (int j = 1; j < n; j++)
{
sum+=(ll)ans[i][j]*j;
}
cout<<sum<<endl;
}
return 0;
}
更新中...