洛谷:P8817
OJ:P5943
方案一:45分
只计算k=0的情况,也就是假设没有跳跃,有DFS就可以。
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | /**************************************************************** * 代码作者: Alex Li * 创建时间: 2025-10-08 03:52:31 * 最后修改: 2025-10-08 15:18:16 * 文件描述: 【22CSPS提高组】假期计划(holiday) k=0的情况 ****************************************************************/ #include <bits/stdc++.h> #include <vector> using namespace std; long long a[2501]; //a[i] 表示景点 i 的分数。 long long maxs=0; vector<int> b[2501];//b[i] 表示与节点 i 直接相连的邻接点(邻接表)。 bool f[2501]; void dfs(int x,int deep, long long sum){ if(deep==5){ if(x==1)maxs=max(maxs,sum); return ; } if(f[x])return ; f[x]=1; for(int i=0;i<b[x].size();i++){ dfs(b[x][i],deep+1,sum+a[x]); } f[x]=0; } int main(){ int n,m,k; cin>>n>>m>>k; if(k==0){ for(int i=2;i<=n;i++)cin>>a[i];//注意编号从 2 开始(a[1] = 0)。 for(int i=0;i<m;i++){// 记录无向图 int u,v; cin>>u>>v; b[u].push_back(v); b[v].push_back(u); } dfs(1,0,0);// 从家出发,走0步,得分0 cout<<maxs; } return 0; } |
方案二:70分
使用邻接表 b[u] 构建图,记录每个节点 u 与其相邻的节点 v。
对于每个节点 x,使用 BFS 构建一张从 x 出发,最多转车 k 次能到达的所有节点集合,保存在 c[x] 中。
这样我们就可以在 DFS 搜索路径时,只考虑合法路径(每段最多转车 k 次)——大大加速搜索。
从家(节点 1)出发,用 DFS 搜索深度为 5 的路径(走 5 段路),路径中不能重复节点(除家可以出现两次),每次只走 c[x] 中可达节点。
在第 5 步(deep == 5)时,检查是否回到了家(x == 1),如果是就更新最大得分。
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | /**************************************************************** * 代码作者: Alex Li * 创建时间: 2025-10-08 03:52:31 * 最后修改: 2025-10-08 15:30:41 * 文件描述: 【22CSPS提高组】假期计划(holiday) k!=0的情况 ****************************************************************/ #include <bits/stdc++.h> #include <queue> #include <vector> using namespace std; long long a[2501],maxs=0; vector<int> b[2501],c[2501];// b[i] 是原图邻接表,c[i] 是最多转 k 次能从 i 到达的点集合 bool f[2501]; void bfs(int x, int k) { bool h[2501] = {0}; // 局部访问标记,防止在 BFS 中重复走节点 queue<pair<int,int>> q; // 队列中存 (当前节点, 当前深度) q.push({x, -1}); h[x] = 1; while (!q.empty()) { int deep = q.front().second; // 到达当前节点的转车次数 int node = q.front().first; q.pop(); if (deep >= k) continue; // 已达最大转车次数限制,停止继续扩展 for (int i = 0; i < b[node].size(); i++) { int to = b[node][i]; if (!h[to]) { c[x].push_back(to); // x 在最多转 k 次内可达 to q.push({to, deep + 1}); h[to] = 1; } } } } void dfs(int x,int deep, long long sum){ if(deep==5){ if(x==1)maxs=max(maxs,sum); return ; } if(f[x])return ; f[x]=1; for(int i=0;i<c[x].size();i++){ dfs(c[x][i],deep+1,sum+a[x]); } f[x]=0; } int main(){ int n,m,k; cin>>n>>m>>k; for(int i=2;i<=n;i++)cin>>a[i]; for(int i=0;i<m;i++){ int u,v; cin>>u>>v; b[u].push_back(v); b[v].push_back(u); } for(int i=1;i<=n;i++)bfs(i,k); dfs(1,0,0); cout<<maxs; return 0; } |
方案三:满分
先预处理出对每个点 i ,同时满足到 i 和到 1 号点距离不超过 k+1 的点中权值前三大的点。为什么是前三大?因为接下来要枚举中间两个点 B,C ,然后 A 从 B 预处理的前三大点中选,D 从 C 预处理的前三大点中选,为了避免 A 和 C,D 重复以及 D 和 A,B 重复。
输入处理:
n(节点数),m(双向直达的点对数量),和 k(每段行程最多的转车次数)。节点编号 1 是家,编号 2~n 是景点。a[] 数组存储每个景点的分数(编号从 2 开始)。编号为 1 的家不需要存储分数。m 条边,表示哪些点之间有直达的公交线路。BFS 距离计算:
bfs(int s) 函数用来从某个节点 s 出发,计算它到所有其他节点的最短路径,最多转车 k 次。d[s][i] 中,表示从节点 s 到节点 i 的最短距离(转车次数)。最大分数的选择:
i,程序通过 BFS 计算它与其他景点之间的最短距离,然后选出分数最高的三个点,存储在 b[i][0], b[i][1], b[i][2] 中。这个过程会跳过与 i 自身相同的点或距离不满足条件的点。主函数逻辑:寻找最大分数:
(i, j) 来计算总分数,分别尝试选择景点 A, B, C, D,并确保每个选择的景点互不相同,且符合转车次数的限制。最终输出最高的分数。满分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | /**************************************************************** * 代码作者: Alex Li * 创建时间: 2023-10-02 19:39:50 * 最后修改: 2025-10-17 19:09:22 * 文件描述: [CSP-S 2022] 假期计划 ****************************************************************/ #include <iostream> #include <queue> #include <vector> using namespace std; typedef long long LL; const int N=2505; int n,m,k; LL a[N]; // 结点的权值,范围是10^18次方,所以需要LL型 vector<int> G[N];//有n个结点m条边, n最大取250,用动态数组存图 int d[N][N];//各点之间的距离,k是条件 int b[N][3];//某点前三大的权值结点 void bfs(int s){ // 求出各点到s点的距离 for(int i=1;i<=n;i++)d[s][i]=-1; queue<int> q; q.push(s),d[s][s]=0; while(q.size()){ int u=q.front(); q.pop(); if(d[s][u]==k)break; for (auto v:G[u]){ if(d[s][v]==-1)d[s][v]=d[s][u]+1; q.push(v); } } } int main(){ cin>>n>>m>>k; k++; for (int i =2; i <=n; i++){ cin>>a[i]; } while(m--){ int u,v; cin>>u>>v; G[u].push_back(v); G[v].push_back(u); } bfs(1); for (int i =2; i <=n; i++){ bfs(i);//宽搜出每个i点与其它结点的权值 int &x1=b[i][0],&x2=b[i][1],&x3=b[i][2]; for (int j = 2; j <=n; j++){ if(i==j)continue; if(d[1][j]!=-1&&d[i][j]!=-1){//选出每个结点前三大权值的结点(不超过K+1) if(a[j]>a[x1]){ x3=x2,x2=x1,x1=j; }else if(a[j]>a[x2]){ x3=x2,x2=j; }else if(a[j]>a[x3]){ x3=j; } } } } LL ans=0; //遍历B、C,然后在B、C后再遍历A、D,只是A、D是在B、C权值最大的三个点里面。所以遍历范围小了。 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]); } } } } cout<<ans; return 0; } |