洛谷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.inholiday/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
posted @ 2026-08-19 17:06  AAA_jiancaipifa  阅读(0)  评论(0)    收藏  举报