【DFS】兴建高铁

这篇博客讨论了一道关于高铁线路规划的问题,目标是使HA省负担的费用最小。题目转化为在无向带权图中寻找一条从0号节点到1号节点的路径,允许忽略两次费用,要求路径城市数量最少。作者指出问题可以通过深度优先搜索(DFS)解决,并详细解释了递归过程和所需记录的关键信息。代码实现附带了详尽的注释。
摘要由CSDN通过智能技术生成

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;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值