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;
}
posted @ 2022-11-20 20:51  Call_me_Eric  阅读(85)  评论(0)    收藏  举报  来源
Live2D