《算法笔记》读书记录DAY_46

本文深入探讨了如何在面对多标尺最短路径问题时,利用Dijkstra算法结合DFS来找到最优解决方案。通过修改Dijkstra算法记录所有最短路径,并通过DFS遍历这些路径,以确定第二标尺最优的路径。详细介绍了算法实现过程和代码模板。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CHAPTER_10  提高篇(4)——图算法专题

10.4.2Dijkstra算法

(接上篇)

上面的题目实际是一个多标尺的最短路径问题。何为多标尺?即题目给出了多个衡量标准,为了输出最终结果,我们不但要考虑道路的距离(第一标尺),还需要考虑每个点的点权(第二标尺)和最短路径的数量(第三标尺)。这种情况我们必须对Dijkstra算法做一些改动才能将其他标尺考虑进来,正如上题所做的处理,我们设置数组weight[]和num[]来记录其他标尺的量,并在更新d[]的值的时候用判断条件来更新这些值。

这种方式成功地让我们解决了上个例题,但是对于一些逻辑更复杂的多标尺最短路径问题,只使用Dijkstra算法就不一定能正确结果(原因是不一定满足最右子结构),或者即便能算出,但因逻辑的复杂导致很容易写错。

这里介绍一种更通用、有模板化的解决此类问题的方式——Dijkstra+DFS。这个方法的思路很简单:先在Dijkstra算法中记录下所有最短路径(只考虑距离),然后从这些最短路径中选出一条第二标尺的最优路径。

(1)使用Dijkstra算法记录所有的最短路径

前面已经介绍了记录一条最短路径的方法——设置pre[]数组。但是考虑到有多条最短路径的情况,每个结点可能存在多个前驱结点,我们要将数组设置成vector类型 “vector<int> pre[maxn]"。这样对每个结点v来说,pre[v]就是一个变长数组vector,里面存放v的所有最短路径的前驱结点。

在本处的Dijkstra算法,只需要考虑距离这一个因素而专心于pre数组的求解。在一开始pre数组不需要赋初值。在更新d[v]的过程中,如果d[u]+Adj[u][j].distance<d[v],说明以u为中介点可以使d[v]更优,此时需要令v的前驱结点为u。并且即便原先pre[v]中存放了若干结点,此时也要全部清空,然后再添加结点u。之后,如果d[u]+Adj[u][j].distance==d[v],说明以u为中介可以找到一条相同距离的路径,因此v的前驱结点需要再原先的基础上添加u。

整个过程的完整代码如下:

vector<int> pre[maxn];         //记录每个顶点的最短路径的前驱 
int d[maxn];                   //记录最短路径 
int n;                         //顶点数 
bool vis[maxn]={0};            //实现SET

struct node {
	int v;
	int pathW;
}; 

vector<node> Adj[maxn];                               //邻接表 

void Dijkstra() {
	fill(d,d+n,INF); 
	d[s]=0;                                           //起点的d[s]为0
	for(int i=0;i<n;i++) {
		int u=-1, MIN=INF;
		for(int j=0;j<n;j++) {                        //找到未访问的最短路径的点 
			if(vis[j]==0&&d[j]<MIN) {
				u=j;
				MIN=d[j];
			}
		}
		if(u==-1)
			return;
		vis[u]=1;
		for(int j=0;j<Adj[u].size();j++) {
			int v=Adj[u][j].v;
			if(vis[v]==0&&d[u]+Adj[u][j].pathW<d[v]) { //以u为中介到v更短 
				d[v]=d[u]+Adj[u][j].pathW;             //优化d[v]    
				pre[v].clear();
				pre[v].push_back(u);                   //令v的前驱为u 
			}
			else if(d[u]+Adj[u][j].pathW==d[v]) {
				pre[v].push_back(u);                    //令v的前驱为u 
			}
		}
	} 
}

(2)遍历所有最短路径,找出一条使第二标尺最优的路径

在用Dijkstra算法得到pre[]数组之后,我们用递归来获得最短路径。前面已经提到,Dijkstra算法最终会生成一棵最短路径树,而这棵最短路径树的信息其实就隐含在pre数组里。

我们只要对某一点v的pre[v]不断递归获取其最短路径上的前驱结点,遍历的过程就会形成一棵递归树,这棵树的根节点就是顶点v,叶子节点都是起点S。当遍历这棵树时,每次到达叶子节点就会产生一条完整的从S到V的最短路径。因此,每得到一条完整的最短路径,就可以对这条路径计算其第二标尺的值(例如累加边权或是点权),令其与当前第二标尺的最优值进行比较。如果比当前最优值更优,则更新最优值,并用这条路径覆盖当前最优路径。这样,当所有路径都遍历完以后,就可以得到最优第二标尺和最优路径。

通过上面的分析,我们如下编写DFS函数模板:

int optValue;                //第二标尺最优值
vector<int> pre[maxn];       //通过Dijkstra生成的pre[]
vector<int> path,tempath;    //最优路径、临时路径 

void DFS(int v) {                    //v为当前访问点 
	//递归边界
	if(v==s) {                       //如果到达了起点S,即叶子结点 
		tempath.push_back(v);        //将起点S加入临时路径最后面
		int value;                   //存放这条最短路径的第二标尺值
		计算路径tempath上的value
		if(value优于optValue) {
			optValue=value;
			path=tempath;            //更新第二标尺最优值和最优路径 
		}
		tempath.pop_back();          //将刚加入的结点删除
		return;
	}
	//递归式
	tempath.push_back(v);            //将当前结点加入临时路径最后面
	for(int i=0;i<pre[v].size();i++) {
		DFS(pre[v][i]);              //向下递归
	}
	tempath.pop_back();               //遍历完所有前驱结点,将当前结点删除 
}

读者会发现,上面DF代码中只有少许部分是需要根据实际情况填充的,即计算tempath上的value和value优于optValue两条语句。这些地方一般会涉及点权和边权的计算。还有一点需要注意,由于递归的原因,tempath和path中的路径结点是逆序的,因此访问结点需要倒着进行。

<think>好的,我现在需要解释用户提供的代码片段`day_date_list.extend(day_datas)`。首先,我应该确认自己对这个语句的理解是否正确。`extend()`是Python中列表的一个方法,用于将一个可迭代对象的所有元素添加到列表末尾。这里的`day_date_list`应该是一个列表,而`day_datas`可能是另一个列表或可迭代对象。 接下来,我需要思考用户可能的疑问点。他们可能想知道`extend()`和`append()`的区别,或者这个操作的具体效果。例如,如果`day_datas`是一个列表,使用`extend()`会将它的元素逐个添加,而`append()`则会将其作为单个元素添加。我需要用例子来说明这一点,比如原来的列表是`[1,2,3]`,`extend([4,5])`会变成`[1,2,3,4,5]`,而`append([4,5])`则变成`[1,2,3,[4,5]]`。 然后,我需要考虑用户的使用场景。可能他们在处理日期数据,比如将多个日期的数据合并到一个列表中。例如,假设`day_date_list`存储了某几天的日期,而`day_datas`是另一批日期,使用`extend()`可以将两者合并。需要提醒用户确保`day_datas`确实是可迭代的,否则会抛出错误,比如如果`day_datas`是整数,就会报错。 另外,要注意`extend()`方法会直接修改原列表,而不是返回新列表。这和`+`操作符不同,后者会创建新列表。例如,`a = a + b`和`a.extend(b)`效果相同,但`extend()`更高效,尤其是处理大数据时。 可能用户在使用过程中遇到了问题,比如元素没有正确添加,或者类型错误。需要指出常见的错误,比如`day_datas`不是可迭代对象,或者误用了`append`导致结构变化。 最后,总结这个语句的作用,强调它用于合并列表元素,并提醒注意事项,比如可迭代性、原地修改等。确保解释清晰,结构合理,帮助用户理解并正确使用`extend()`方法。</think>我们来逐步解释代码`day_date_list.extend(day_datas)`的作用和原理: --- ### 1. **基本定义** - `day_date_list`:是一个Python列表(List)对象 - `day_datas`:需要被合并到`day_date_list`中的另一个可迭代对象(如列表、元组、集合等) --- ### 2. **`.extend()`方法的作用** - **功能**:将`day_datas`中的所有元素**逐个添加**到`day_date_list`的末尾 - **与`.append()`的区别**: - `.append(x)`:将整个`x`作为单个元素添加到列表 - `.extend(x)`:将`x`中的每个元素拆开后再逐个添加 #### 示例对比: ```python a = [1, 2, 3] b = [4, 5] # 使用 append a.append(b) # 结果: [1, 2, 3, [4, 5]] # 使用 extend a.extend(b) # 结果: [1, 2, 3, 4, 5] ``` --- ### 3. **典型使用场景** 假设你正在处理日期数据: ```python day_date_list = ["2023-10-01", "2023-10-02"] day_datas = ["2023-10-03", "2023-10-04"] day_date_list.extend(day_datas) # 结果: ["2023-10-01", "2023-10-02", "2023-10-03", "2023-10-04"] ``` --- ### 4. **注意事项** 1. **可迭代性要求**:`day_datas`必须是可迭代对象(如列表、元组),否则会抛出`TypeError` ```python # 错误示例(整数不可迭代) day_datas = 100 day_date_list.extend(day_datas) # TypeError ``` 2. **原地修改**:`.extend()`会直接修改原列表`day_date_list`,而不是返回新列表 3. **性能优势**:相比`+`操作符(如`new_list = a + b`),`.extend()`更高效,尤其适合合并大型列表 --- ### 5. **等价实现** 若不用`.extend()`,可以用循环模拟: ```python for item in day_datas: day_date_list.append(item) ``` --- ### 总结 `day_date_list.extend(day_datas)`的作用是:将`day_datas`中的所有元素逐个添加到`day_date_list`末尾,最终实现两个列表的**扁平化合并**。使用时需确保`day_datas`为可迭代对象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值