题意:给出一个单向带权图和一个点s,求点u,u到s的最短路径和s到u的最短路径之和最大。
思路:
对于s到任意点的最短路,直接dijkstra可以求出。
对于任意点到s的最短路,将所有边反向然后求一次最短路。容易证明,求出的s到任意点v的最短路s-->v就是原来没有反向的图的v-->s的最短路。
所以先求一次dijkstra,保存此次的结果,然后把所有的边反向,权值不变,再求一次dijkstra,将两次结果加起来,计算它们之中的最大值。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 1010
#define inf 0x3f3f3f3f
using namespace std;
int n, m;
int value[maxn][maxn];
int dis[maxn];
bool vis[maxn];
int dis2[maxn];
void dijkstra(int s)
{
memset(vis, false, sizeof(vis));
for (int i = 1; i <= n; i++) dis[i] = inf;
dis[s] = 0;
while(true)
{
int v = -1;
for (int j = 1; j <= n; j++)
{
if (!vis[j] && (v == -1 || dis[j]< dis[v])) v = j;
}
if (v == -1) break;
vis[v] = true;
for (int j = 1; j <= n; j++)
{
if (dis[j] > dis[v] + value[v][j])
{
dis[j] = dis[v] + value[v][j];
}
}
}
}
void dijkstra2(int s)
{
memset(vis, false, sizeof(vis));
for (int i = 1; i <= n; i++) dis2[i] = inf;
dis2[s] = 0;
while (true)
{
int v = -1;
for (int j = 1; j <= n; j++)
{
if (!vis[j] && (v == -1 || dis2[j]< dis2[v])) v = j;
}
if (v == -1) break;
vis[v] = true;
for (int j = 1; j <= n; j++)
{
if (dis2[j] > dis2[v] + value[j][v])
{
dis2[j] = dis2[v] + value[j][v];
}
}
}
}
int main()
{
int aim;
while (scanf("%d %d %d", &n, &m, &aim) != EOF)
{
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
value[i][j] = inf;
while (m--)
{
int u, v, w; cin >> u >> v >> w;
if (w < value[u][v]) value[u][v] = w;
}
dijkstra(aim);
dijkstra2(aim);
int ans = 0;
for (int i = 1; i <= n; i++)
if (dis[i] != inf && dis2[i] != inf)
ans = max(ans, dis[i] + dis2[i]);
printf("%d
", ans);
}
return 0;
}
注:这里的dijkstra用的是一般方法,复杂度(V^2),也可以用最小堆(优先队列)处理,复杂度(E log(V))