Description
中央钦定在HA省建立国家级中心城市群,刚获得国家拨款兴建高铁,高铁的起止城市是中央决定的,中途可能经过
若干城市。根据国家拨款的政策,国家将负担费用最大的两个区间,其余的必须由HA省负担。假如高铁线路中途只
经过一个城市,国家只负担费用较大的区间。假如是直达的,国家将不负担任何费用。你被省里钦定为这个高铁兴
建项目的总工程师,必须规划出一条高铁线路,使得HA省负担的费用最少。当然,路线上每个城市最多只经过一次
Input
第一行是一个正整数n,n<=50代表每对城市之间的高铁建设费用估算
(注意并非每对城市之间的建设费用都进行了估算)。
接下来n行是用空格分隔的三个整数s,e,c。s和e代表城市的编码,
高铁的起点和终点城市分别是编码为0和1,其余的城市依次按顺序编码。
c<=1000,是在s和e之间建设费用估算,从s到e与从e到s的建设费用是相同的。
Output
输出只有一行,格式为c1c2…cmcost,各数字用一个空格分隔,代
表高铁线路规划和省负担的费用。
ci代表城市编码(注意c1=0,cm=1),cost是费用。我们保证输入肯定
有解,如果有多个解,输出当中经过城市最少的解,如果仍有多个解,则输出
当中按字典序排列最小的解。
Sample Input
12 0 7 10 8 1 2 1 9 7 1 7 10 8 4 5 3 4 8 6 3 12 5 6 15 6 9 7 2 3 1 0 2 3 5 0 14
Sample Output
0 2 3 4 8 1 6
分析
本蒟蒻表示看到这题的第一反应是最短路。QAQ可能是最近最短路做的有点多。
这道题其实是一个比较简单的DFS,可以使用递归的形式来完成
首先我们来梳理下题意。
题目的意思大概是说,给定一个无向带权图,给你两次忽略当前边长度的机会,你需要在这个图上找到从0号节点到1号节点的一条路径,使得这条路径上所有边的权值和最小。
然后我们来想一下需要那些参数
首先最长的两条边的长度一定是要记录下来的;
因为可能在任意一层递归更新答案,所以当前走过的所有路径边权和也要记录下来;
题目要求找一条从0去到1的路径,那么我们当前走到哪个点了也需要记录下来;
题目还要求如果存在多个解,输出经过城市最少的,所以当前经过的城市数量也要记录下来
然后,这道题就比较清晰了。
其余都在代码注释中
有史以来写得最详细的注释QAQ
代码
#include<bits/stdc++.h>
using namespace std;
int n;
int g[57][57];
int v[57], w[57], p[57];
int ans = INT_MAX;
int cnt = INT_MAX;
bool vis[57];
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 ^ 48); ch = getchar();}
return x * f;
}
//当前城市编号 目前所有高铁轨道中的最大值 目前~的最小值 花费 修建了几条高铁
void dfs(int now, int mx1, int mx2, int cost, int pa) {
p[pa] = now;//记录走过的城市
if (now == 1) {
int pp;
//处理修建数量不足
switch(pa) {
case 1: pp = cost; break;//只经过了一条边,政府不出钱
case 2: pp = cost - mx1; break;//只经过两条边,政府出一条的钱
default: pp = cost - mx1 - mx2;//经过两条边以上,政府出两条的钱
}
//找到更优解 or 经过城市更少 更新答案
if (pp < ans || ((pp == ans) && (pa < cnt))) {
ans = pp;//记录权值和
cnt = pa;//记录经过城市数
memcpy(w, p, sizeof p);//记录经过的城市编号,等价于下一行
// for (int i = 1; i <= pa; i++) w[i] = p[i];
}
}
for (int i = 1; i <= n; i++) {
if (g[now][i] && !vis[i]) {//存在边且未访问过
vis[i] = true;
//递归 分三种情况讨论
//当前边比所经过的所有边中的最大边还要大
if (g[now][i] >= mx1) dfs(i, g[now][i], mx1, cost + g[now][i], pa + 1);
//当前边在所经过的所有边中最大边和次大边之间
else if (g[now][i] >= mx2) dfs(i, mx1, g[now][i], cost + g[now][i], pa + 1);
//当前边无法更新最大边和次大边
else dfs(i, mx1, mx2, cost + g[now][i], pa + 1);
vis[i] = false;//回溯
}
}
}
int main() {
n = read();
for (int i = 1; i <= n; i++) {
int s = read(), e = read(), c = read();
g[s][e] = g[e][s] = c;//无向边
}
vis[0] = true;//从0号节点开始
dfs(0, 0, 0, 0, 0);
cout << 0;
for (int i = 1; i <= cnt; i++) printf(" %d", w[i]);
printf(" %d", ans);
return 0;
}