洛谷P8817 [CSP-S 2022] 假期计划
P8817 [CSP-S 2022] 假期计划
题目描述
小熊的地图上有 \(n\) 个点,其中编号为 \(1\) 的是它的家、编号为 \(2, 3, \ldots, n\) 的都是景点。部分点对之间有双向直达的公交线路。如果点 \(x\) 与 \(z_1\)、\(z_1\) 与 \(z_2\)、……、\(z_{k - 1}\) 与 \(z_k\)、\(z_k\) 与 \(y\) 之间均有直达的线路,那么我们称 \(x\) 与 \(y\) 之间的行程可转车 \(k\) 次通达;特别地,如果点 \(x\) 与 \(y\) 之间有直达的线路,则称可转车 \(0\) 次通达。
很快就要放假了,小熊计划从家出发去 \(4\) 个不同的景点游玩,完成 \(5\) 段行程后回家:家 \(\to\) 景点 A \(\to\) 景点 B \(\to\) 景点 C \(\to\) 景点 D \(\to\) 家且每段行程最多转车 \(k\) 次。转车时经过的点没有任何限制,既可以是家、也可以是景点,还可以重复经过相同的点。例如,在景点 A \(\to\) 景点 B 的这段行程中,转车时经过的点可以是家、也可以是景点 C,还可以是景点 D \(\to\) 家这段行程转车时经过的点。
假设每个景点都有一个分数,请帮小熊规划一个行程,使得小熊访问的四个不同景点的分数之和最大。
输入格式
第一行包含三个整数 \(n, m, k\),分别表示地图上点的个数、双向直达的点对数量、每段行程最多的转车次数。
第二行包含 \(n - 1\) 个正整数,分别表示编号为 \(2, 3, \ldots, n\) 的景点的分数。
接下来 \(m\) 行,每行包含两个正整数 \(x, y\),表示点 \(x\) 和 \(y\) 之间有道路直接相连,保证 \(1 \le x, y \le n\),且没有重边,自环。
输出格式
输出一个正整数,表示小熊经过的 \(4\) 个不同景点的分数之和的最大值。
输入输出样例 #1
输入 #1
8 8 1
9 7 1 8 2 3 6
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 1
输出 #1
27
输入输出样例 #2
输入 #2
7 9 0
1 1 1 2 3 4
1 2
2 3
3 4
1 5
1 6
1 7
5 4
6 4
7 4
输出 #2
7
说明/提示
【样例解释 #1】
当计划的行程为 \(1 \to 2 \to 3 \to 5 \to 7 \to 1\) 时,\(4\) 个景点的分数之和为 \(9 + 7 + 8 + 3 = 27\),可以证明其为最大值。
行程 \(1 \to 3 \to 5 \to 7 \to 8 \to 1\) 的景点分数之和为 \(24\)、行程 \(1 \to 3 \to 2 \to 8 \to 7 \to 1\) 的景点分数之和为 \(25\)。它们都符合要求,但分数之和不是最大的。
行程 \(1 \to 2 \to 3 \to 5 \to 8 \to 1\) 的景点分数之和为 \(30\),但其中 \(5 \to 8\) 至少需要转车 \(2\) 次,因此不符合最多转车 \(k = 1\) 次的要求。
行程 \(1 \to 2 \to 3 \to 2 \to 3 \to 1\) 的景点分数之和为 \(32\),但游玩的并非 \(4\) 个不同的景点,因此也不符合要求。
【样例 #3】
见附件中的 holiday/holiday3.in 与 holiday/holiday3.ans。
【数据范围】
对于所有数据,保证 \(5 \le n \le 2500\),\(1 \le m \le 10000\),\(0 \le k \le 100\),所有景点的分数 \(1 \le s_i \le {10}^{18}\)。保证至少存在一组符合要求的行程。
| 测试点编号 | \(n \le\) | \(m \le\) | \(k \le\) |
|---|---|---|---|
| \(1 \sim 3\) | \(10\) | \(20\) | \(0\) |
| \(4 \sim 5\) | \(10\) | \(20\) | \(5\) |
| \(6 \sim 8\) | \(20\) | \(50\) | \(100\) |
| \(9 \sim 11\) | \(300\) | \(1000\) | \(0\) |
| \(12 \sim 14\) | \(300\) | \(1000\) | \(100\) |
| \(15 \sim 17\) | \(2500\) | \(10000\) | \(0\) |
| \(18 \sim 20\) | \(2500\) | \(10000\) | \(100\) |
思路:
这题是典型的邻接表+无向图
主要就是用bfs求出最短距离
还有be数组的功能确定最大分数
然后输出就可以了
ACcode:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2505;
int n,m,k;
ll s[N],ans=0;
vector<int> g[N]; //邻接表无向图的存储
int d[N][N],be[N][3]; //d[i][j]:i和j两点的最短路劲长度,be[i][j],j∈[0,2]:景点i的前三个最优候选景点
//判断一下景点a到b能否在k次转车内到达
bool is(int a,int b){
return (d[a][b]!=-1 and d[a][b]<=k+1);
}
//bfs广搜出从一点pos到所有点的最短距离
void bfs(int pos){
memset(d[pos],-1,sizeof(d[pos]));
queue<int> q;
q.push(pos);
d[pos][pos]=0;
while(!q.empty()){
int u=q.front();
q.pop();
for(auto v:g[u]){
if(d[pos][v]==-1){ //第一次访问到这个距离
d[pos][v]=d[pos][u]+1;
q.push(v);
}
}
}
}
//尝试用景点j去算出b[i][0~2]
//条件:1.j不是i,2.j也不是家(1),3.i,j可到达
void f(int i,int j){
if(j==i or j==1) return ;
if(!is(1,j) or !is(j,i)) return ; //不满足k次转车限制也pass掉
//使用j景点替换b[i]中低分的景点,保持前三和降序
for(int p=0;p<3;p++){
if(be[i][p]==0 or s[j]>s[be[i][p]]){
//从前往后不断去掉最小的,加入更大的
for(int q=2;q>p;q--) be[i][q]=be[i][q-1];
be[i][p]=j;
break;
}
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m>>k;
for(int i=2;i<=n;i++) cin>>s[i];
while(m--){
int x,y;
cin>>x>>y;
g[x].push_back(y);
g[y].push_back(x);
}
//处理每个点的BFS
for(int i=1;i<=n;i++) bfs(i);
//枚举i,j预处理出b数组
for(int i=2;i<=n;i++)
for(int j=2;j<=n;j++)
f(i,j);
//然后枚举中间景点B,C
for(int b=2;b<=n;b++){
for(int c=2;c<=n;c++){
//相同或者不满足转车的都跳过
if(b==c) continue;
if(!is(b,c) or !is(c,b)) continue;
//接着,从b点开始,选一个最大分数的a点出来(b[b]中选a)
//同理,b[c]中选d
//枚举BC最好写的原因就是全都可以根据邻接表连接起来
for(int x=0;x<3;x++){
int a=be[b][x];
if(a==0) continue; //没有候选就跳了呗
for(int y=0;y<3;y++){
int d=be[c][y];
if(d==0) continue; //依旧
//剩下就确保四个景点互不相等就可以了
if(a!=b and a!=c and a!=d and b!=c and b!=d and c!=d){
ll tmp=s[a]+s[b]+s[c]+s[d];
ans=max(ans,tmp);
}
}
}
}
}
cout<<ans<<endl;
return 0;
}
//Author:AAA_jiancaipifa

浙公网安备 33010602011771号