AIGC标识 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 互不相同
posted @ 2026-09-08 13:11  Zhenggeek  阅读(6)  评论(0)    收藏  举报