[CSP-S 2022] 假期计划
小熊的地图上有 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 个不同景点的分数之和的最大值。
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
27
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
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 个不同的景点,因此也不符合要求。
【数据范围】
对于所有数据,保证 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 |
根据题目描述,要求的是从家出发去 4 4 4 个不同的景点游玩,完成 5 5 5 段行程后回家,在每段行程转车不超过 k k k次的情况下,经过的 4 4 4 个景点的分数之和的最大值。
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int N = 2505;
int n, m, k;
vector<int> g[N];
int st[N][N], dis[N];
LL w[N];
void bfs(int s)
{
queue<int> q;
memset(dis, 0x3f, sizeof dis);
dis[s] = 0;
q.push(s);
while(q.size())
{
int u = q.front(); q.pop();
//如果起点到u点的距离到达k+1,需要转车k次,就不能继续扩展了
if(dis[u] == k + 1) continue;
for(int v : g[u])
{
if(dis[v] > dis[u] + 1)
{
dis[v] = dis[u] + 1;
st[s][v] = true; //从起点到v转车不超过k次
q.push(v);
}
}
}
}
int main()
{
scanf("%d%d%d", &n, &m, &k);
for(int i = 2; i <= n; i ++) scanf("%lld", &w[i]);
for(int i = 0; i < m; i ++)
{
int a, b;
scanf("%d%d", &a, &b);
g[a].push_back(b), g[b].push_back(a);
}
for(int i = 1; i <= n; i ++)
bfs(i);
LL ans = 0;
//暴力枚举4个不同的景点
for(int a = 2; a <= n; a ++)
{
if(!st[1][a]) continue;
for(int b = 2; b <= n; b ++)
{
if(!st[a][b]) continue;
for(int c = 2; c <= n; c ++)
{
if(!st[b][c] || a == c) continue;
for(int d = 2; d <= n; d ++)
{
if(!st[d][1] || !st[c][d] || a == d || b == d) continue;
ans = max(ans, w[a] + w[b] + w[c] + w[d]);
}
}
}
}
printf("%lld\n", ans);
return 0;
}
从数据范围来分析, 5 ≤ n ≤ 2500 5 \le n \le 2500 5≤n≤2500,因此算法的时间复杂度应控制在 O ( n 2 ) O(n^2) O(n2),也就是说至多枚举两个景点。对于行程 1 → a → b → c → d → 1 1 \to a \to b \to c \to d \to 1 1→a→b→c→d→1来说,可以枚举中间两个景点,也就是 b , c b,c b,c。那么如何确定 a a a和 d d d呢?
首先, a a a和 d d d要满足下面的条件:
这里不妨设 f ( x ) f(x) f(x)表示所有与 1 1 1点转车不超过 k k k次、与 x x x点转车不超过 k k k次、并且不是 x x x的 所有景点的集合,那么 a ∈ f ( b ) a\in f(b) a∈f(b), d ∈ f ( c ) d\in f(c) d∈f(c)。通过bfs很容易构造出 f ( x ) f(x) f(x)。
其次, f ( x ) f(x) f(x)需要保留所有满足条件的景点吗?例如 a ∈ f ( b ) a\in f(b) a∈f(b),那么 f ( b ) f(b) f(b)需要保留所有与 1 1 1点转车不超过 k k k次、与 b b b点转车不超过 k k k次、并且不是 b b b的所有景点吗?
基于上述分析,对于 a a a和 d d d只需要枚举集合 f ( b ) f(b) f(b)和 f ( c ) f(c) f(c),由于每个集合只有 3 3 3个景点,总的时间复杂度为 O ( n 2 × 9 ) O(n^2\times 9) O(n2×9)。
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int N = 2505;
int n, m, k;
vector<int> g[N];
//f[i]表示所有与1点转车不超过k次、与i点转车不超过k次、并且不是i的所有景点的集合
int st[N][N], dis[N], f[N][4];
LL w[N];
void bfs(int s, int f[])
{
queue<int> q;
memset(dis, 0x3f, sizeof dis);
dis[s] = 0;
q.push(s);
while(q.size())
{
int u = q.front(); q.pop();
if(dis[u] == k + 1) continue;
for(int v : g[u])
{
if(dis[v] > dis[u] + 1)
{
dis[v] = dis[u] + 1;
st[s][v] = true; //从起点到v转车不超过k次
q.push(v);
if(st[1][v]) //如果1到v转车不超过k次
{
f[3] = v;
//插入排序向前调整
for(int i = 3; i > 0 && w[f[i]] > w[f[i - 1]]; i --)
swap(f[i], f[i - 1]);
}
}
}
}
}
int main()
{
scanf("%d%d%d", &n, &m, &k);
for(int i = 2; i <= n; i ++) scanf("%lld", &w[i]);
for(int i = 0; i < m; i ++)
{
int a, b;
scanf("%d%d", &a, &b);
g[a].push_back(b), g[b].push_back(a);
}
for(int i = 1; i <= n; i ++)
bfs(i, f[i]);
LL ans = 0;
for(int b = 2; b <= n; b ++)
{
for(int c = 2; c <= n; c ++)
{
if(!st[b][c]) continue;
for(int i = 0; i < 3; i ++)
for(int j = 0; j < 3; j ++)
{
int a = f[b][i], d = f[c][j];
//a和d存在
if(a && d && a != d && a != c && b != d)
ans = max(ans, w[a] + w[b] + w[c] + w[d]);
}
}
}
printf("%lld\n", ans);
return 0;
}