贪心_迪杰斯特拉算法

贪心法

  • 基本思想:从问题的某⼀个初始解出发,在每⼀个阶段都根据贪⼼策略来做出当前最优的决策,逐步逼近给定的⽬标,尽可能快地求得更好的解。当达到算法中的某⼀步不能再继续前进时,算法终⽌。
  • 基本要素
    • 最优⼦结构性质
      • 当⼀个问题的最优解⼀定包含其⼦问题的最优解时
      • 采⽤反证法证明
    • 贪⼼选择性质
      • 所求问题的整体最优解可以通过⼀系列局部最优的选择获得,即通过⼀系列的逐步局部最优选择使得最终的选择⽅案是全局最优的
  • 得出的结论
    • 每个阶段⾯临选择时, 贪⼼法都做出对眼前来讲是最有利的选择
    • 选择⼀旦做出不可更改,即不允许回溯
    • 根据贪⼼策略来逐步构造问题的解

Dijkstra算法

  • 思想:按各个顶点与源点之间路径⻓度的递增次序,⽣成源点到各个顶点的最短路径的⽅法,即先求出⻓度最短的⼀条路径,再参照它求出⻓度次短的⼀条路径,依此类推,直到从源点到其它各个顶点的最短路径全部求出为⽌。
  • 算法设计:
    • s : 源点。集合S中的顶点到源点的最短路径的⻓度已经确定,集合V-S中所包含的顶点到源点的最短路径的⻓度待定。
    • 特殊路径:从源点出发只经过S中的点到达V-S中的点的路径。
    • 贪⼼策略:选择特殊路径⻓度最短的路径,将其相连的V-S中的顶点加⼊到集合S中。
  • 伪代码表示
    dist[s]=0;path[s]=s;
    定义存放edge类型的元素的优先队列变量pq;
    将edge(from, to, weight) 加入到pq中;
    while(pq非空)
    {
        队首元素出队,其顶点编号记为(e.to);
        检测(e.to)是否第一次出队,如果不是,直接执行下一次循环,如果是则进行如下操作;
        更新边权;
        记录(e.to)结点是由哪个点指向的;
        依次找到与队首元素邻接的各顶点v,计算源点到v的距离,如果新的距离比dist[v]小,则将dist[v]赋值为新的距离,并将edge(e.to, v[e.to][i].to, v[e.to][i].weight + dist[e.to])加入pq中。
    }
    
  • 这里特别注意,用到了v[e.to][i].weight + dist[e.to],由当前点到源点的最短距离,加上边权,而不是单纯的边权入队。
  • 这里的优先队列就是贪心的思想,每次取的都是目前在队列中到源点最小的距离weight,从(from → \rightarrow to)的边

这里有一个例题,见下图:
在这里插入图片描述
求解过程的表格:
dist数组变化过程

SV-Sdist[1]dist[2]dist[3]dist[4]dist[5]
初始{0}{1,2,3,4}8-1-1-1-1
1{0,1}{2,3,4}8-123-1-1
2{0, 1, 3}{2,4}82423-1-1
3{0, 1, 3}{2,4}8242330-1

p数组变化过程:

012345
初始0-1-1-1-1-1
100-1-1-1-1
200-11-1-1
30011-1-1
400113-1

详细完整代码如下

#include <iostream>
#include <queue>
#include <vector>

using namespace std;

#define MAX 1005

struct edge {
	// 从哪个结点开始
	int from;
	// 指向哪个结点
	int to;
	// 边权
	int weight;
	// 默认构造
	edge(int from, int to, int weight) {
		this->from = from;
		this->to = to;
		this->weight = weight;
	}
};

// 为了使用优先队列,需要对 '<' 进行重载
bool operator<(const edge &e1, const edge &e2) {
	// 从小到大排列
	return e1.weight > e2.weight;
}

// 用邻接链表的方式对图进行存储
vector<edge> v[MAX];
// 存储下标index的点到源点的最短距离
int dist[MAX];
// 记录路径
int p[MAX];
// 源点
int s = 0;
// 点的个数
int num_point = 0;

// 预处理,dist数组中的元素在未被访问过之前全部标注为-1,含义是无穷大
void pre_solve() {
	for (int i = 0; i < num_point; i++) {
		dist[i] = -1;
		p[i] = -1;
	}
}

// 打印路径
void path(int i) {
	// 递归回溯答应
	if (i == s) {
		// 回溯到源点为止
		printf("%d\n", s);
		return;
	}
	if (i == -1) {
		printf("此路不通\n");
		return ;
	}
	printf("%d<-", i);
	path(p[i]);
}

int main() {
	int n = 0;
	priority_queue<edge> pq;

	printf("请输入点的数量 : ");
	scanf("%d", &num_point );
	// 进行预处理
	pre_solve();


	printf("请输入边的条数 : ");
	scanf("%d", &n );

	int from, to, weight;
	for (int i = 0; i < n; i++) {
		// 读入数据,建图
		scanf("%d%d%d", &from, &to, &weight);
		v[from].push_back(edge(from, to, weight));
	}

	printf("请输入源点 : ");
	scanf("%d", &s);
	// 源点到源点的距离是0
	dist[s] = 0;
	// 源点的上一个没有,这里就赋值为他自己,便于回溯
	p[s] = s;

	/**
	 * 将源点相连的边全部加入优先队列中
	 * 同时,将p(路径数组)的上一个全部置为源点
	 */
	int len = v[s].size();
	for (int i = 0; i < len; i++) {
		pq.push(v[s][i]);
		p[v[s][i].to] = s;
	}

	// 当队列空的时候,表明所有的边都已经访问过,才退出
	while (pq.empty() == false ) {
		edge e = pq.top();
		pq.pop();
		/**
		 * 如果dist数组中,index = e.to位置的元素已经访问过了,就不需要再访问一遍了,
		 * 因为,源点到index点的最短距离,如果有,那么只会出现在第一次出队的时候,
		 * 后面出队的,一定没有前面出队的距离短————优先队列的效果
		 */
		if (dist[e.to] != -1) {
			continue;
		}
		// 此时是第一次出队,边权赋值
		dist[e.to] = e.weight;
		// 记录路径
		p[e.to] = e.from;
		len = v[e.to].size();
		// 将与其相连的所有边全部放到优先队列中
		for (int i = 0; i < len; i++) {
			pq.push(edge(e.to, v[e.to][i].to, v[e.to][i].weight + dist[e.to]));
		}
	}

	// 打印相应结点的路径和距离信息
	for (int i = 0; i < num_point; i++) {
		if (dist[i] == -1) {
			printf("点%d无法从源点到达,距离无穷大\n", i);
			continue;
		}
		printf("到源点的距离为 : %d ", dist[i]);
		path(i);
	}


	return 0;
}

运行结果:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

中小庸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值