动态规划、记忆化搜索、Dijkstra算法的总结

动态规划

动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。具体的动态规划算法多种多样,但它们具有相同的填表格式。

动态规划一般可分为线性动规,区域动规,树形动规,背包动规四类。

举例:

线性动规:拦截导弹,合唱队形,挖地雷,建学校,剑客决斗等;

区域动规:石子合并, 加分二叉树,统计单词个数,炮兵布阵等;

树形动规:贪吃的九头龙,二分查找树,聚会的欢乐,数字三角形等;

背包问题:01背包问题,完全背包问题,分组背包问题,二维背包,装箱问题,挤牛奶(同济ACM第1132题)等

适用条件

任何思想方法都有一定的局限性,超出了特定条件,它就失去了作用。同样,动态规划也并不是万能的。适用动态规划的问题必须满足最优化原理和无后效性。

1.最优化原理(最优子结构性质) 最优化原理可这样阐述:一个最优化策略具有这样的性质,不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略总是最优的。一个问题满足最优化原理又称其具有最优子结构性质。

2.无后效性将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策,而只能通过当前的这个状态。换句话说,每个状态都是过去历史的一个完整总结。这就是无后向性,又称为无后效性。

3.子问题的重叠性 动态规划将原来具有指数级时间复杂度的搜索算法改进成了具有多项式时间复杂度的算法。其中的关键在于解决冗余,这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术,它在实现的过程中,不得不存储产生过程中的各种状态,所以它的空间复杂度要大于其它的算法。

记忆化搜索   记忆化搜索=搜索的形式+动态规划的思想

记忆化搜索:算法上依然是搜索的流程,但是搜索到的一些解用动态规划的那种思想和模式作一些保存。

一般说来,动态规划总要遍历所有的状态,而搜索可以排除一些无效状态。

更重要的是搜索还可以剪枝,可能剪去大量不必要的状态,因此在空间开销上往往比动态规划要低很多。

记忆化算法在求解的时候还是按着自顶向下的顺序,但是每求解一个状态,就将它的解保存下来,

以后再次遇到这个状态的时候,就不必重新求解了。

这种方法综合了搜索和动态规划两方面的优点,因而还是很有实用价值的

Dijkstra算法

迪杰斯特拉算法是从一个顶点到其余各顶点的最短路径算法,解决的是有向图中最短路径问题。迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。

原理

1.首先,引入一个辅助向量D,它的每个分量 D

表示当前所找到的

Dijkstra算法运行动画过程

从起始点

(即源点

)到其它每个顶点

的长度。

例如,D[3] = 2表示从起始点到顶点3的路径相对最小长度为2。这里强调相对就是说在算法执行过程中D的值是在不断逼近最终结果但在过程中不一定就等于长度。[1]

2.D的初始状态为:若从

有弧(即从

存在连接边),则D

为弧上的权值(即为从

的边的权值);否则置D

为∞。

显然,长度为 D

= Min{ D |

∈V } 的路径就是从

出发到顶点

的长度最短的一条路径,此路径为(

)。

3.那么,下一条长度次短的是哪一条呢?也就是找到从源点

到下一个顶点的最短路径长度所对应的顶点,且这条最短路径长度仅次于从源点

到顶点

的最短路径长度。

假设该次短路径的终点是

,则可想而知,这条路径要么是(

),或者是(

)。它的长度或者是从

的弧上的权值,或者是D

加上从

的弧上的权值。

4.一般情况下,假设S为已求得的从源点

出发的最短路径长度的顶点的集合,则可证明:下一条次最短路径(设其终点为

)要么是弧(

),或者是从源点

出发的中间只经过S中的顶点而最后到达顶点

的路径。

因此,下一条长度次短的的最短路径长度必是D

= Min{ D

|

∈V-S },其中D

要么是弧(

)上的权值,或者是D

(

∈S)和弧(

,

)上的权值之和。

算法描述如下:

1)令arcs表示弧上的权值。若弧不存在,则置arcs为∞(在本程序中为MAXCOST)。S为已找到的从

出发的的终点的集合,初始状态为空集。那么,从

出发到图上其余各顶点

可能达到的长度的初值为D=arcs[Locate Vex(G,

)],

∈V;

2)选择

,使得D

=Min{ D |

∈V-S } ;

3)修改从

出发的到集合V-S中任一顶点

的最短路径长度。[1]

问题描述

无向图 G=(V,E) 中,假设每条边 E[i] 的长度为 w[i],找到由顶点 V0 到其余各点的最短值。[2]

算法思想

按路径长度递增次序产生算法:

把顶点集合V分成两组:

(1)S:已求出的顶点的集合(初始时只含有源点V0)

(2)V-S=T:尚未确定的顶点集合

将T中顶点按递增的次序加入到S中,保证:

(1)从源点V0到S中其他各顶点的长度都不大于从V0到T中任何顶点的最短路径长度

(2)每个顶点对应一个距离值

S中顶点:从V0到此顶点的长度

T中顶点:从V0到此顶点的只包括S中顶点作中间顶点的最短路径长度

依据:可以证明V0到T中顶点Vk的,或是从V0到Vk的直接路径的权值;或是从V0经S中顶点到Vk的路径权值之和

反证法可证)

求最短路径步骤

算法步骤如下:

G={V,E}

1. 初始时令 S={V0},T=V-S={其余顶点},T中顶点对应的距离值

若存在<V0,Vi>,d(V0,Vi)为<V0,Vi>弧上的权值

若不存在<V0,Vi>,d(V0,Vi)为∞

2. 从T中选取一个与S中顶点有关联边且权值最小的顶点W,加入到S中

3. 对其余T中顶点的距离值进行修改:若加进W作中间顶点,从V0到Vi的距离值缩短,则修改此距离值

重复上述步骤2、3,直到S中包含所有顶点,即W=Vi为止

算法实现

下面是该算法的C语言实现[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

#include<stdio.h>

#include<stdlib.h>

#define max 11000000000

inta[1000][1000];

intd[1000];//d表示某特定边距离

intp[1000];//p表示永久边距离

inti,j,k;

intm;//m代表边数

intn;//n代表点数

intmain()

{

scanf("%d%d",&n,&m);

intmin1;

intx,y,z;

for(i=1;i<=m;i++)

{

scanf("%d%d%d",&x,&y,&z);

a[x][y]=z;

a[y][x]=z;

}

for(i=1;i<=n;i++)

d[i]=max1;

d[1]=0;

for(i=1;i<=n;i++)

{

min1=max1;

for(j=1;j<=n;j++)

if(!p[j]&&d[j]<min1)

{

min1=d[j];

k=j;

}

p[k]=j;

for(j=1;j<=n;j++)

if(a[k][j]!=0&&!p[j]&&d[j]>d[k]+a[k][j])

d[j]=d[k]+a[k][j];

}

for(i=1;i<n;i++)

printf("%d->",p[i]);

printf("%d\n",p[n]);

return0;

}

大学经典教材<<数据结构>>(C语言版 严蔚敏 吴为民 编著) 中该算法的实现


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

/*

测试数据 教科书 P189 G6 的邻接矩阵 其中 数字 1000000 代表无穷大

6

1000000 1000000 10 100000 30 100

1000000 1000000 5 1000000 1000000 1000000

1000000 1000000 1000000 50 1000000 1000000

1000000 1000000 1000000 1000000 1000000 10

1000000 1000000 1000000 20 1000000 60

1000000 1000000 1000000 1000000 1000000 1000000

结果:

D[0]   D[1]   D[2]   D[3]   D[4]   D[5]

 0   1000000   10     50     30     60

*/

#include <iostream>

#include <cstdio>

#define MAX 1000000

using namespace std;

int arcs[10][10];//邻接矩阵

int D[10];//保存最短路径长度

int p[10][10];//路径

int final[10];//若final[i] = 1则说明 顶点vi已在集合S中

int n = 0;//顶点个数

int v0 = 0;//源点

int v,w;

void ShortestPath_DIJ()

{

     for (v = 0; v < n; v++) //循环 初始化

     {

          final[v] = 0; D[v] = arcs[v0][v];

          for (w = 0; w < n; w++) p[v][w] = 0;//设空路径

          if (D[v] < MAX) {p[v][v0] = 1; p[v][v] = 1;}

     }

     D[v0] = 0; final[v0]=0; //初始化 v0顶点属于集合S

     //开始主循环 每次求得v0到某个顶点v的最短路径 并加v到集合S中

     for (int i = 1; i < n; i++)

     {

          int min = MAX;

          for (w = 0; w < n; w++)

          {

               //我认为的核心过程--选点

               if (!final[w]) //如果w顶点在V-S中

               {

                    //这个过程最终选出的点 应该是选出当前V-S中与S有关联边

                    //且权值最小的顶点 书上描述为 当前离V0最近的点

                    if (D[w] < min) {v = w; min = D[w];}

               }

          }

          final[v] = 1; //选出该点后加入到合集S中

          for (w = 0; w < n; w++)//更新当前最短路径和距离

          {

               /*在此循环中 v为当前刚选入集合S中的点

               则以点V为中间点 考察 d0v+dvw 是否小于 D[w] 如果小于 则更新

               比如加进点 3 则若要考察 D[5] 是否要更新 就 判断 d(v0-v3) + d(v3-v5) 的和是否小于D[5]

               */

               if (!final[w] && (min+arcs[v][w]<D[w]))

               {

                    D[w] = min + arcs[v][w];

                   // p[w] = p[v];

                    p[w][w] = 1; //p[w] = p[v] + [w]

               }

          }

     }

}

int main()

{

    cin >> n;

    for (int i = 0; i < n; i++)

    {

         for (int j = 0; j < n; j++)

         {

              cin >> arcs[i][j];

         }

    }

    ShortestPath_DIJ();

    for (int i = 0; i < n; i++) printf("D[%d] = %d\n",i,D[i]);

    return 0;

}

时间: 2024-08-24 08:22:57

动态规划、记忆化搜索、Dijkstra算法的总结的相关文章

HDU 1142 A Walk Through the Forest (记忆化搜索+Dijkstra算法)

A Walk Through the Forest Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 7583    Accepted Submission(s): 2791 Problem Description Jimmy experiences a lot of stress at work these days, especial

sicily 1176. Two Ends (Top-down 动态规划+记忆化搜索 v.s. Bottom-up 动态规划)

DescriptionIn the two-player game "Two Ends", an even number of cards is laid out in a row. On each card, face up, is written a positive integer. Players take turns removing a card from either end of the row and placing the card in their pile. T

Codevs_1017_乘积最大_(划分型动态规划/记忆化搜索)

描述 http://codevs.cn/problem/1017/ 给出一个n位数,在数字中间添加k个乘号,使得最终的乘积最大. 1017 乘积最大 2000年NOIP全国联赛普及组NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 今年是国际数学联盟确定的“2000——世界数学年”,又恰逢我国著名数学家华罗庚先生诞辰90周年.在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,你的一个好朋友

动态规划①——记忆化搜索

首先的首先,必须明白动态规划(DP)以后很有用很有用很有用很有用……首先的其次,必须明白:动规≍搜索=枚举 一.最简单的记忆化搜索(应该可以算DP) 题目(来自洛谷OJ)http://www.luogu.org/problem/show?pid=1434# [不麻烦大家自己找了]题目描述 DescriptionMichael喜欢滑雪.这并不奇怪,因为滑雪的确很刺激.可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你.Michael想知道在一个区域中最

UVA_437_The_Tower_of_the_Babylon_(DAG上动态规划/记忆化搜索)

描述 https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=378 n种方块,给出每一种的长宽高,现在要落起来,上面的方块的长和宽要严格小于下面的方块,问最多落多高. ACM Contest Problems ArchiveUniversity of Valladolid (SPAIN)437 The Tower of BabylonPerhap

动态规划-记忆化搜索

1.数字三角形 学习链接:http://blog.csdn.net/zwhlxl/article/details/46225947 输入样例: 5 7 3 8 8 1 0 2 7 4 4 4 5 2 6 5 输出样例: 30 递归代码: #include <stdio.h> #include <memory.h> #include <math.h> #include <string> #include <vector> #include <

着色方案(动态规划+记忆化搜索)

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1079 AC代码: 1 /* 2 直接状态压缩是显然是不可行的,我们考虑如果没有相邻颜色不相同的限制的话, 3 如果两种油漆能染的木块数目相同,我们就可以认为两种油漆无差别. 4 设dp[a1][a2][a3][a4][a5]为能染1个木块的油漆有a1种……的方案数. 5 但是有相邻颜色的限制,如果上一次用了颜色数为k的油漆, 6 那么这一次有一种颜色数为k-1的油漆就不能用了,转移的时

算法导论学习-动态规划之记忆化搜索

一. 动态规划 动态规划(dynamic programming),与“分治思想”有些相似,都是利用将问题分 为子问题,并通过合并子问题的解来获得整个问题的解.于“分治”的不同之处在 于,对于一个相同的子问题动态规划算法不会计算第二次,其实现原理是将每一个计算过的子问题的值保存在一个表中. 二. 记忆化搜索 我们常见的动态规划问题,比如流水线调度问题,矩阵链乘问题等等都是“一步接着一步解决的”,即规模为 i 的问题需要基于规模 i-1 的问题进行最优解选择,通常的递归模式为DP(i)=optim

HDU 1142 A Walk Through the Forest (Dijkstra + 记忆化搜索 好题)

A Walk Through the Forest Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 6350    Accepted Submission(s): 2332 Problem Description Jimmy experiences a lot of stress at work these days, especial