题意:
给一个n个点m条边的无向图,和k个特殊点
要你从k个特殊点选出两个点连边,问连边之后1到n的最短路距离最大是多少
思路:
先计算出:
点1到每个点的最短距离d[0][i]
点n到每个点的最短距离d[1][i]
假设选取的点是i,j,那么有两种情况:
1. 1->i->j->n,距离为d[0][i]+1+d[1][j]
2. 1->j->i->n,距离为d[0][j]+1+d[1][i]
连接i,j之后的最短路距离为他们的min
可以想到一种解法是枚举i和j,然后取max就是本题答案
但是复杂度是O(n^2),显然不可取,考虑如何优化
假设min是第一种情况,即1->i->j->n
则d[0][i]+1+d[1][j]<=d[0][j]+1+d[1][i]
移项得d[0][i]-d[1][i]<=d[0][j]-d[1][j]
即i到1和n的最短路距离之差更小则i一定在j的前面
因此我们对于k个特殊点用d[0][k]-d[1][k]从小到大排序
这样就满足排序之后前面的点i一定在后面的点j之前
这样就可以O(n)枚举j了,满足条件的i是j之前的所有数
因为当前答案为d[0][i]+1+d[1][j]
对于j前面的所有数i的d[0][i]取个max再加上d[1][j]+1就是当前j的最大答案
对所有答案取max就是本题答案
ps:因为题目的边权都为1,所以可以直接用bfs计算最短路
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=2e5+5;
vector<int>g[maxm];
int a[maxm];
int mark[maxm];
int d[2][maxm];//d[0][i]表示1到i的最短路距离,d[1][i]表示n到i的最短路距离
int n,m,k;
void bfs(int st,int id){
queue<int>q;
q.push(st);
for(int i=1;i<=n;i++)mark[i]=0;
mark[st]=1;
while(!q.empty()){
int x=q.front();
q.pop();
for(int v:g[x]){
if(mark[v])continue;
d[id][v]=d[id][x]+1;
q.push(v);
mark[v]=1;
}
}
}
signed main(){
cin>>n>>m>>k;
for(int i=1;i<=k;i++){
cin>>a[i];
}
for(int i=1;i<=m;i++){
int a,b;
cin>>a>>b;
g[a].push_back(b);
g[b].push_back(a);
}
bfs(1,0);
bfs(n,1);
vector<pair<int,int> >temp;
for(int i=1;i<=k;i++){
temp.push_back({d[0][a[i]]-d[1][a[i]],d[1][a[i]]});
}
sort(temp.begin(),temp.end());
int ans=0;
int ma=0;
for(int i=0;i<k;i++){
if(i){
ans=max(ans,ma+temp[i].second+1);
}
ma=max(ma,temp[i].first+temp[i].second);//键+值=d[0][i];
}
cout<<min(d[0][n],ans)<<endl;//还要和原图的最短路取个min
return 0;
}