AcWing 3797. 最大化最短路(排序优化枚举)
3797. 最大化最短路
题意
给定一个无向图,所有边权都为1,给定 \(k\) 个特殊点。
现要在这 \(k\) 个特殊点中选定两个连一条权值为1边,求连边后从 \(1\) 到 \(N\) 的最短路最大值是多少。
思路
正反跑一次bfs,可以发现在特殊点 \(i,j\) 之间加一条边后会产生一条长度为 \(min(dis1[i] + disn[j] + 1\) , \(dis1[j] + disn[i] + 1)\) 的新路径。
最暴力的想法就是枚举这两个点,显然时间复杂度无法接受,现在关键在如何优化枚举过程。
对 $$min(dis1[i] + disn[j] + 1 , dis1[j] + disn[i] + 1)$$
我们假设先到 \(i\) 再到 \(j\) 更优,上式变形为 $$ dis1[i] - disn[i] < dis1[j] - disn[j] $$
也就是说,对任意的 \(i,j\) 满足上式就说明了先经过 \(i\) 再经过 \(j\) 更优,而这个式子左右两边的变量只有一个,就很方便进行排序。
我们将特殊点按上式排序后,可发现对于特殊点 \(i\) 的任意前缀 \(j\) ,一定是先过 \(i\) 再过 \(j\) 更优,那么只要维护这个前缀 max,就可以找到先经过任意特殊点 \(i\) 的新加入路径的最大值。
最后将原来最短路和处理出来的全局 \(max\) 就是答案。
#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define lowbit(x) x&-x
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
void solve()
{
int N,M,K; cin >> N >> M >> K;
vector<int> p(K + 1);
for(int i = 1;i <= K;i ++) cin >> p[i];
vector<vector<int>> adj(N + 1);
for(int i = 0;i < M;i ++) {
int u,v; cin >> u >> v;
adj[u].push_back(v), adj[v].push_back(u);
}
auto bfs = [&](int s) {
vector<int> dis(N + 1,-1);
dis[s] = 0;
queue<int> q; q.push(s);
while(q.size()) {
int u = q.front(); q.pop();
for(auto v : adj[u]) if(dis[v] == -1) {
dis[v] = dis[u] + 1;
q.push(v);
}
}
return dis;
};
auto dis1 = bfs(1);
auto disn = bfs(N);
sort(p.begin() + 1,p.end(),[&](int x,int y) {
return dis1[x] - disn[x] > dis1[y] - disn[y];
});
int ans = 0;
for(int i = 2,mx = disn[p[1]];i <= K;i ++) ans = max(ans,dis1[p[i]] + mx + 1), mx = max(mx,disn[p[i]]);
cout << min(dis1[N],ans) << endl;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
//int T;cin>>T;
//while(T--)
solve();
return 0;
}