CSPS2022补题--假期计划
假期计划
首先简化题意: 选定4个点, 然后安排好这四个点的游览顺序, 接着需要让中间的边权\(\le k\), 并且让总分数最大
很显然, 我们一开始的时候就可以说去做一次floyd-- > \(O(n^3)\), 然后通过枚举这4个点的方式--> \(O(n ^ 4)\)来找到答案, 这个做法可以获得\(40\)分的好成绩,跑路了 ~~
但是很明显这个东西我们可以优化, 而我们发现这张图的边权为\(1\), 而众所周知--> 对于边权只有1或者说0和1的图我们可以通过bfs/0-1 bfs来实现单次\(O(n)\) 的复杂度, 然后我们可以枚举每一个点去做bfs, 现在我们就把这个floyd的\(O(n ^ 3)\)优化到了\(O(n ^ 2)\)了, 然后我们可以发现对于这道题目, 我们\(O(n ^ 2)\)的算法是可以接受的, 所以我们考虑优化后面枚举四个点的\(O(n ^ 4)\)
那么我们很容易发现, 我们可以说考虑每个点与其他点对的最大价值, 而且对于每一个点, 我们都可以说去存下来, 然后每个点我们可以记录下来他可以在\(k\)步内完成的、 价值奉献最大的3个点--> 为什么是3个点呢?(不过其实我们用4个点也可以,更多的也都可以)
首先这个东西不可能是自己, 所以我们需要存的话就是把除自己外的4个点存下来, 然而我们注意到, 对于每一个点, 我们都可以去往多个点, 而对于一个点来说, 如果它是1的话, 我们需要从A、B、C、D中选择一个尽可能大的, 而对于后面的A, 我们只需要3个点, 对于后面也是同理, 那么我们可以只需要枚举这B和C, 并且确保这两个点可以在k次及以内的转车到达, 这样的话, 我们就把整个东西缩减到了只需要看3个点就可以了, 然后我们就做完了
详细一点说:
- 首先我们枚举这个B和C也就是中间是为了转移
- 那么现在我们只需要知道这个A和D就可以了
- 而对于这个A和C的约束以A为例, 我们需要满足家可以到达A, 而A还可以到达B
- 公式化来说就是--> \(1- >A -> B\)
- 当我们对于每个点的可以到达的排好序后, 就可以知道我们最多只有2个事固定的B/C, 那么就会有一个是可以用的, 这样的话就刚刚好, 并且也不会漏掉最优解
Code :
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 3010;
int n, m, k;
vector<int> e[N], f[N];
ll dis[N], w[N];
bool vis[N][N];
bool cmp(int x, int y) {
return w[x] > w[y];
}
void bfs(int u) {
memset(dis, -1, sizeof(dis));
queue<int> q;
q.push(u);
dis[u] = 0;
while(q.size()) {
int v = q.front(); q.pop();
if(v != u) {
vis[u][v] = 1;
if(u != 1 && vis[1][v]) {
f[u].push_back(v);
sort(f[u].begin(), f[u].end(), cmp);
if(f[u].size() > 3) {
f[u].pop_back();
}
}
}
if(dis[v] == k + 1) {
continue;
}
for(int t : e[v]) {
if(dis[t] == -1) {
dis[t] = dis[v] + 1;
q.push(t);
}
}
}
}
signed main() {
ios::sync_with_stdio(0); cin.tie(0);
cin >> n >> m >> k;
for(int i = 2;i <= n;i++) {
cin >> w[i];
}
for(int i = 1;i <= m;i++) {
int x, y;
cin >> x >> y;
e[x].push_back(y);
e[y].push_back(x);
}
for(int i = 1;i <= n;i++) {
bfs(i);
}
ll ans = 0;
for(int b = 2;b <= n;b++) {
for(int c = 2;c <= n;c++) {
if(vis[b][c]) {
for(int a : f[b]) {
for(int d : f[c]) {
if(a != b && a != c && a != d && b != c && b != d && c != d) {
ans = max(ans, w[a] + w[b] + w[c] + w[d]);
}
}
}
}
}
}
cout << ans;
return 0;
}
Qwen's think
题解:P8817 [CSP-S 2022] 假期计划
一、题意简述
小熊从家(点 1)出发,依次游览 4 个不同的景点 A → B → C → D,最后回家。每段行程(共 5 段)最多转车 \(k\) 次(即最短路径 \(\le k+1\) 条边)。求 4 个景点分数之和的最大值。
二、核心思路
1. 求可达性:BFS 替代 Floyd
题目中图的边权均为 1,因此从每个点做一次 BFS 即可求出 \(k\) 步内所有可达点,复杂度 \(O(n(n+m))\),远优于 Floyd 的 \(O(n^3)\)。
2. 枚举策略:固定 B、C,查表找 A、D
行程结构为:家 → A → B → C → D → 家
- 枚举中间两点 B、C,要求 B、C 在 \(k\) 次转车内可达。
- A 的候选条件:家可达 A,且 A 可达 B。
- D 的候选条件:家可达 D,且 C 可达 D。
- 对每个点预处理出满足条件的、分数最高的若干候选点,枚举时直接查表。
3. 为什么只需保留前 3 个候选点?
以 A 的候选池为例,A 必须满足:① 家可达 A;② A 可达 B。
- 固定 B、C 后,A 只需在候选池中选 1 个,且不能与 B、C、D 重复。
- 最坏情况:候选池分数最高的前 2 名恰好是 B 和 C(或其中一个是 D),导致前 2 名不可用。
- 结论:只要保留前 3 名,第 3 名一定不是 B、C、D 中的任何一个,必能选出合法且分数最高的 A。
因此保留 3 个点即可保证不漏最优解。
4. 最多可以用到多少个点?
- 理论上限:最多用到 4 个点。当 B、C、D 三个点恰好占据了候选池的前 3 名时,只有第 4 名才是不与它们重复的合法选择。
- 实际限制:受 \(n \le 2500\) 约束,若每个点存 4 个候选点,总空间为 \(2500 \times 4 = 10000\),内存完全可接受。
- 工程建议:代码中直接保留 4 个点更稳妥,既保证绝对不漏解,又不会增加最终枚举的时间复杂度(最终枚举仍为 \(O(n^2 \times 16)\),瓶颈在 BFS)。
三、复杂度分析
| 环节 | 复杂度 |
|---|---|
| BFS 求可达性 | \(O(n(n+m))\) |
| 预处理候选点 | \(O(n^2 \log n)\)(排序) |
| 枚举 B、C 并查表 | \(O(n^2 \times 9)\)(保留3个时) |
| 总复杂度 | \(O(n(n+m) + n^2 \log n)\) |
四、总结
| 环节 | 关键点 |
|---|---|
| 求可达性 | BFS 替代 Floyd,利用边权为1的性质 |
| 枚举策略 | 枚举 B、C,查表找 A、D |
| 保留点数 | 理论 3 个够用,代码建议写 4 个更稳妥 |
| 去重 | 确保 A、B、C、D 互不相同 |

浙公网安备 33010602011771号