CSP-S2022T1题解
题目链接:点这里
[CSP-S 2022] 假期计划
题目背景
由于众所周知的原因,官方数据现置于子任务 0,剩余的子任务为民间数据。
题目描述
小熊的地图上有 n n n 个点,其中编号为 1 1 1 的是它的家、编号为 2 , 3 , … , n 2, 3, \ldots, n 2,3,…,n 的都是景点。部分点对之间有双向直达的公交线路。如果点 x x x 与 z 1 z_1 z1、 z 1 z_1 z1 与 z 2 z_2 z2、……、 z k − 1 z_{k - 1} zk−1 与 z k z_k zk、 z k z_k zk 与 y y y 之间均有直达的线路,那么我们称 x x x 与 y y y 之间的行程可转车 k k k 次通达;特别地,如果点 x x x 与 y y y 之间有直达的线路,则称可转车 0 0 0 次通达。
很快就要放假了,小熊计划从家出发去 4 4 4 个不同的景点游玩,完成 5 5 5 段行程后回家:家 → \to → 景点 A → \to → 景点 B → \to → 景点 C → \to → 景点 D → \to → 家且每段行程最多转车 k k k 次。转车时经过的点没有任何限制,既可以是家、也可以是景点,还可以重复经过相同的点。例如,在景点 A → \to → 景点 B 的这段行程中,转车时经过的点可以是家、也可以是景点 C,还可以是景点 D → \to → 家这段行程转车时经过的点。
假设每个景点都有一个分数,请帮小熊规划一个行程,使得小熊访问的四个不同景点的分数之和最大。
输入格式
第一行包含三个正整数 n , m , k n, m, k n,m,k,分别表示地图上点的个数、双向直达的点对数量、每段行程最多的转车次数。
第二行包含 n − 1 n - 1 n−1 个正整数,分别表示编号为 2 , 3 , … , n 2, 3, \ldots, n 2,3,…,n 的景点的分数。
接下来 m m m 行,每行包含两个正整数 x , y x, y x,y,表示点 x x x 和 y y y 之间有道路直接相连,保证 1 ≤ x , y ≤ n 1 \le x, y \le n 1≤x,y≤n,且没有重边,自环。
输出格式
输出一个正整数,表示小熊经过的 4 4 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 → 2 → 3 → 5 → 7 → 1 1 \to 2 \to 3 \to 5 \to 7 \to 1 1→2→3→5→7→1 时, 4 4 4 个景点的分数之和为 9 + 7 + 8 + 3 = 27 9 + 7 + 8 + 3 = 27 9+7+8+3=27,可以证明其为最大值。
行程 1 → 3 → 5 → 7 → 8 → 1 1 \to 3 \to 5 \to 7 \to 8 \to 1 1→3→5→7→8→1 的景点分数之和为 24 24 24、行程 1 → 3 → 2 → 8 → 7 → 1 1 \to 3 \to 2 \to 8 \to 7 \to 1 1→3→2→8→7→1 的景点分数之和为 25 25 25。它们都符合要求,但分数之和不是最大的。
行程 1 → 2 → 3 → 5 → 8 → 1 1 \to 2 \to 3 \to 5 \to 8 \to 1 1→2→3→5→8→1 的景点分数之和为 30 30 30,但其中 5 → 8 5 \to 8 5→8 至少需要转车 2 2 2 次,因此不符合最多转车 k = 1 k = 1 k=1 次的要求。
行程 1 → 2 → 3 → 2 → 3 → 1 1 \to 2 \to 3 \to 2 \to 3 \to 1 1→2→3→2→3→1 的景点分数之和为 32 32 32,但游玩的并非 4 4 4 个不同的景点,因此也不符合要求。
【样例 #3】
见附件中的 holiday/holiday3.in 与 holiday/holiday3.ans。
【数据范围】
对于所有数据,保证 5 ≤ n ≤ 2500 5 \le n \le 2500 5≤n≤2500, 1 ≤ m ≤ 10000 1 \le m \le 10000 1≤m≤10000, 0 ≤ k ≤ 100 0 \le k \le 100 0≤k≤100,所有景点的分数 1 ≤ s i ≤ 10 18 1 \le s_i \le {10}^{18} 1≤si≤1018。保证至少存在一组符合要求的行程。
| 测试点编号 | n ≤ n \le n≤ | m ≤ m \le m≤ | k ≤ k \le k≤ |
|---|---|---|---|
| 1 ∼ 3 1 \sim 3 1∼3 | 10 10 10 | 20 20 20 | 0 0 0 |
| 4 ∼ 5 4 \sim 5 4∼5 | 10 10 10 | 20 20 20 | 5 5 5 |
| 6 ∼ 8 6 \sim 8 6∼8 | 20 20 20 | 50 50 50 | 100 100 100 |
| 9 ∼ 11 9 \sim 11 9∼11 | 300 300 300 | 1000 1000 1000 | 0 0 0 |
| 12 ∼ 14 12 \sim 14 12∼14 | 300 300 300 | 1000 1000 1000 | 100 100 100 |
| 15 ∼ 17 15 \sim 17 15∼17 | 2500 2500 2500 | 10000 10000 10000 | 0 0 0 |
| 18 ∼ 20 18 \sim 20 18∼20 | 2500 2500 2500 | 10000 10000 10000 | 100 100 100 |
考场思路
当时看到这道题时,满脑子都是 O(
n
3
{n}^{3}
n3)的传递闭包,于是喜提70分凎的真蚌
正解
其实这道题可以用O(
n
{n}
n)的时间复杂度找到任意一点走k个边能到达的点,然后O(
n
2
{n}^{2}
n2)预处理一下所有点,因为1点是确定的废话,而1点相邻的有A点和D点,所以可以在上面预处理时确定这个点能到达的最大权值的点……
然鹅注意细节 “从家出发去
4
4
4 个不同的景点游玩”,所以有可能出现你找到的最大权值点是相同的情况,于是要找最大权值,次大权值,第三大权值,然后就完事了
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + ch - '0'; ch = getchar();}
return x * f;
}
int n, m, k;
const int maxn = 2510;
vector<int> e[maxn];
int a[maxn], d[maxn][maxn];
int b[maxn][3];
void bfs(int s){
for(int i = 1;i <= n;i++)d[s][i] = -1;
queue<int> que;
que.push(s);d[s][s] = 0;
while(!que.empty()){
int u = que.front(); que.pop();
if(d[s][u] == k)continue;
for(int i = 0, len = e[u].size();i < len;i++){
int v = e[u][i];
if(d[s][v] == -1){
d[s][v] = d[s][u] + 1;
que.push(v);
}
}
}
}
signed main(){
n = read(); m = read(); k = read() + 1;
for(int i = 2;i <= n;i++)a[i] = read();
for(int i = 1;i <= m;i++){
int u = read(), v = read();
e[u].push_back(v); e[v].push_back(u);
}
bfs(1);
for(int i = 2;i <= n;i++){
bfs(i);
for(int j = 2;j <= n;j++){
if(i == j)continue;
if(d[i][j] == -1 || d[1][j] == -1)continue;
if(a[j] > a[b[i][0]]){b[i][2] = b[i][1];b[i][1] = b[i][0];b[i][0] = j;}
else if(a[j] > a[b[i][1]]){b[i][2] = b[i][1];b[i][1] = j;}
else if(a[j] > a[b[i][2]]){b[i][2] = j;}
}
}
int ans = 0;
for(int i = 2;i <= n;i++){
for(int j = i + 1;j <= n;j++){
if(d[i][j] == -1)continue;
for(int k1 = 0;k1 < 3;k1++){
for(int k2 = 0;k2 < 3;k2++){
int x = b[i][k1], y = b[j][k2];
if(x == 0 || y == 0 || x == y || x == i || x == j || y == i || y == j)continue;
ans = max(ans,a[i] + a[j] + a[x] + a[y]);
}
}
}
}
printf("%lld\n",ans);
return 0;
}

浙公网安备 33010602011771号