D105 BFS最短路 P8817 [CSP-S 2022] 假期计划
D105 BFS最短路 P8817 [CSP-S 2022] 假期计划_哔哩哔哩_bilibili
无向图,从顶点 1 出发经过 4 个景点 5 段链,最后回到 1,要求每段链上的中间点不超过 k 个,最大化 4 个景点的点权和。

思路
很明显条件 k 是约束最短路的,我们先跑 BFS 预处理每个点为起点的最短路 $d_{i,j}$
所选路径构成一个环,设 4 个景点为 $a、b、c、d$,那么路径 $1-a-b-c-d-1$ 的每一段的边数应 $\le k+1$
我们需要知道每个点通过哪些点可以到达 1?先预处理每个点的前 3 大可达点,如果点 i 通过点 j 可达 1,就记录点 j
开个有序集数组 set,一边记录,一边清除点权小的


怎样枚举 4 个点呢?先枚举最远的两个点 $b、c$,再枚举 $b$ 的可达点,再枚举 $c$ 的可达点,每一步都要判合法性

时间复杂度 $O(n^2*3^2)$
// 多起点最短路+set优选 BFS 算法 O(n^2*3^3) #include<bits/stdc++.h> #define rep(i,l,r) for(int i=l;i<=r;++i) #define rop(i,l,r) for(int i=l;i>=r;--i) #define ll long long using namespace std; const int N=2505; vector<int> e[N]; ll w[N],ans; int n,m,k,d[N][N]; set<pair<ll,ll>> st[N]; void bfs(int s,int *d){ rep(i,1,n) d[i]=2e9; d[s]=0; queue<int> q; q.push(s); while(!q.empty()){ int u=q.front(); q.pop(); for(auto v:e[u]){ if(d[v]>d[u]+1) d[v]=d[u]+1,q.push(v); } } } int main(){ scanf("%d%d%d",&n,&m,&k); rep(i,2,n) scanf("%lld",&w[i]); rep(i,1,m){ int u,v; scanf("%d%d",&u,&v); e[u].push_back(v); e[v].push_back(u); } rep(i,1,n) bfs(i,d[i]); //预处理每个点的最短路 rep(i,2,n) rep(j,2,n){ //预处理每个点的前3大可达点 if(j==i) continue; if(d[i][j]<=k+1&&d[1][j]<=k+1) st[i].insert({w[j],j}); //如果i通过j可达1,就记录点j if(st[i].size()>3) st[i].erase(st[i].begin()); //保留i可达1的前3大点权 } rep(b,2,n) rep(c,2,n) if(b!=c&&d[b][c]<=k+1){ //如果b、c不同且可达 for(auto [wa,a]:st[b]) if(a!=c){ //如果b的可达点不是c for(auto [wd,d]:st[c]) if(d!=b&&d!=a){ //如果c的可达点不是b也不是a ans=max(ans,w[b]+w[c]+wa+wd); //更新路径1-a-b-c-d-1的点权和 } } } printf("%lld\n",ans); return 0; }
浙公网安备 33010602011771号