D105 BFS最短路 P8817 [CSP-S 2022] 假期计划

D105 BFS最短路 P8817 [CSP-S 2022] 假期计划_哔哩哔哩_bilibili

 

P8817 [CSP-S 2022] 假期计划 - 洛谷

无向图,从顶点 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,一边记录,一边清除点权小的

image

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

image

时间复杂度 $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;
}

 

posted @ 2026-03-12 18:45  董晓  阅读(53)  评论(0)    收藏  举报