题意:
给你一张有向图,要你求其他点到目标点x并且从目标点返回出发点的距离最小值的最大值。
思路:
我们学的是单源最短路,所以算其他点到目标点的距离不太现实,我们不妨转换视角,来个逆向思维,从其他点到目标点相当于把有向图反转后,从目标点到其他点。这样就可以解决去的问题,回的问题就是常规思路。所以先算回的最短路,然后路径反转,算去的最短路,然后两者相加就是往返最小值。最后比较即可。
这里我写了两个版本,一个是优先队列优化的,还有一个就是没有优化的。
优先队列优化的版本
AC code:
#include<iostream>
#include<cstdio>
#include<vector>
#include<bitset>
#include<stack>
#include<set>
#include<queue>
#include<map>
#include<cmath>
#include<string>
#include<cstring>
#include<ctime>
#include<fstream>
#include<cstdlib>
#include<algorithm>
using namespace std;
//typedef pair<int, int> pii;
#define pii pair<int,int>
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a))
#define per(i,a,b) for(int i=a;i<=b;i++)
#define rep(i,a,b) for(int i=a;i>=b;i--)
#define all(x) x.begin(),x.end()
#define PER(i,x) for(auto i=x.begin();i!=x.end();i++)
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
typedef long long LL;
const double eps=1.0e-5;
const int maxn=1e3 + 10;
int dx[4] = {0,0,-1,1};
int dy[4] = {1,-1,0,0};
int n = 0,m = 0,x = 0;
int adj[maxn][maxn];
int d1[maxn],d2[maxn];
bool used[maxn];
struct cmp{//cmp类型,然后重新定义比较方式
bool operator()(const pii a,const pii b){
return a.second > b.second;//second小值优先
}
};
void dijikstra(int s,int* dist){
fill(used,used+maxn,false);
fill(dist,dist+maxn,INF);
priority_queue<pii,vector<pii>,cmp > que;
dist[s] = 0;//初始值设置
que.push(pii(s,0));
while(!que.empty()){
pii p = que.top();que.pop();//从未访问过的节点中选取距离远点最短的(更新距离的那些点)
int u = p.first,d = p.second;
if(dist[u] < d || used[u] == true){//如果一直最小值比d小,或者已经访问过,就直接跳过
continue;
}
for(int i = 1;i <= n;++i){//更新周围点的最短路径
used[u] = true;
if(dist[i] > dist[u] + adj[u][i] && used[i] == false){
dist[i] = dist[u] + adj[u][i];
que.push(pii(i,dist[i]));
}
}
}
}
int main(){
#ifndef ONLINE_JUDGE
//freopen("a.txt","r",stdin);
#endif
while(~scanf("%d %d %d",&n,&m,&x)){
fill(adj[0],adj[0] + maxn*maxn,INF);
per(i,1,m){
int a = 0,b = 0,t = 0;
scanf("%d %d %d",&a,&b,&t);
adj[a][b] = t;
}
dijikstra(x,d1);
per(i,1,n){//路径反转
per(j,i+1,n){//j要从i+1开始,否则反转两次,相当于没变化
swap(adj[i][j],adj[j][i]);
}
}
dijikstra(x,d2);
int maxv = 0;
per(i,1,n){
if(d1[i] != INF && d2[i] != INF){//必须加上这个判断条件
maxv = max(maxv , d1[i] + d2[i]);
}
}
printf("%d\n",maxv);
}
return 0;
}
未用优先队列优化的版本:
AC code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 1e8
using namespace std;
const int maxn = 1000 + 10;
int vis[maxn];
int d[maxn],d1[maxn];
int map[maxn][maxn];
int n = 0,m = 0,x = 0;
void dijikstra(int s){
d[s] = 0;
while(true){
int v = -1;
for(int i = 1;i <= n;++i){
if(vis[i] == false && (v == -1 || d[i] < d[v])){
v = i;
}
}
if(v == -1){
break;
}
vis[v] = true;
for(int i = 1;i <= n;++i){
if(vis[i] == false && (d[v] + map[v][i]) < d[i]){
d[i] = d[v] + map[v][i];
}
}
}
}
void init(){
fill(d,d+maxn,INF);
fill(vis,vis+maxn,false);
fill(map[0],map[0]+maxn*maxn,INF);
}
void traverse(){//路径反转
for(int i = 1;i <= n;++i){
for(int j = i+1;j <= n;++j){//j不能从1开始,否则反转两次,相当于没变化
swap(map[i][j],map[j][i]);
}
}
}
int main(){
scanf("%d %d %d",&n,&m,&x);
init();
for(int i = 0;i < m;++i){
int a = 0,b = 0,w = 0;
scanf("%d %d %d",&a,&b,&w);
map[a][b] = w;
}
dijikstra(x);
for(int j = 1;j <= n;++j){//储存第一次的运算结果,并且还原d,vis数组
d1[j] = d[j];
d[j] = INF;
vis[j] = false;
}
traverse();
dijikstra(x);
int maxv = 0;
for(int j = 1;j <= n;++j){
if(d[j] + d1[j] > maxv){
maxv = d[j] + d1[j];
}
}
printf("%d\n",maxv);
return 0;
}