动态规划和回溯法的异同

一:举例

回溯法典型的题目有:八皇后问题,老鼠走迷宫问题.(老鼠问题

动态规划典型题目有:最长公共子序列问题,还有滑雪路径问题(滑雪路径

这些都是我做过的几道题,对两种算法有点感悟,所以写出自己的第一个博客。(算是转载吧,自己写一点分析而已)

二:以老鼠走迷宫问题和最长滑雪路径问题为例来分析两种算法,因为在做最长滑雪路径问题时,我立刻想到了老鼠走迷宫这个问题,也说明了他们的相同之处,但也因此让我更深刻认识到了不同。(代码主要来源于链接博主)

老鼠问题:

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define N 8
#define M 9
int maze[9][9] = { { 2,2,2,0,2,2,2,0,0 },//迷宫
{ 2,0,0,0,0,0,2,0,0 },
{ 2,0,2,2,2,2,2,2,2 },
{ 0,0,0,0,0,0,0,0,2 },
{ 2,0,2,2,2,2,0,2,2 },
{ 2,0,2,2,0,0,0,2,2 },
{ 2,0,2,2,0,2,2,0,2 },
{ 2,0,2,0,0,0,0,0,0 },
{ 2,0,2,2,2,2,2,2,2 }
};
typedef struct point   //表示起始出发点和终点
{
int x, y;
}point;

void Available(point a,point c, int array[M][M]);
void mouse(int array[M][M], point exit1, point entrance);

point entrance;//入口位置
point exit1;//出口位置

int equals(point a, point b)   //判断是否到了终点
{
if ((a.x == b.x)&&(a.y == b.y))
return 1;
else
return 0;
}
void print(int array[M][M])   //打印矩阵,1代表路径
{
for (int i = 0; i < M; i++) {
for (int j = 0; j < M; j++)
{
printf("%d ", array[i][j]);
}
printf("\n");
}
}

void Available(point a,point c, int array[M][M])    
{
array[a.x][a.y] = 1;         //先赋值,注意本函数的注释最后一句话
point b;
if (a.y > 0)
{
b.x = a.x, b.y = a.y - 1;
mouse(array, b, c);
}


if (a.y < N)
{
b.x = a.x, b.y = a.y + 1;
mouse(array, b, c);
}
if (a.x > 0)
{
b.x = a.x - 1, b.y = a.y;
mouse(array, b, c);
}
if (a.x < N)
{
b.x = a.x + 1, b.y = a.y;
mouse(array, b, c);
}
if(equals(b,c)!=1)         
array[a.x][a.y] = 0;//如果一条路走到黑没有到达希望的终点,那么回溯到最开始的位置
return;
}
void mouse(int array[M][M], point exit1, point entrance)  //老鼠走一步路
{
if (equals(exit1, entrance))
{
print(array);
return;
}
else if(array[exit1.x][exit1.y]==0)
Available(exit1,entrance, array);
}


void main()
{
entrance.x = 0, entrance.y = 3;
exit1.x = 7, exit1.y = 8;
mouse(maze, exit1, entrance);
system("pause");
}

这里的mouse函数和availble函数互相调用。尤其注意availble函数的注释,回溯法特点是走一次可以判断结果是否是对的,而动态规划问题与问题之间存在关联,也就是常说的子问题。你需要记录子问题的一些结果。

最长滑雪路径问题:

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
 
int h[101][101];//输入的高度值   
int dis_sk[101][101];//记录了每个点可以滑行的最大距离   
int dx[] = { -1,1,0,0 };//为了方便上下左右侧的滑行的最大距离而使用的方便数组   
int dy[] = { 0,0,-1,1 };
int r, c;//输入的行和列


bool in_bound(int i, int j) {
return i >= 0 && i < r && j >= 0 && j < c;
}

int dis(int i, int j) {
int temp;
if (dis_sk[i][j]) //如果已经求出来了,直接返回   
return dis_sk[i][j];
for (int k = 0; k < 4; k++) {
if (in_bound(i + dx[k], j + dy[k])) {  //如果没有越界   
if (h[i][j] > h[i + dx[k]][j + dy[k]]) {     //如果顺着该侧可以滑   
temp = dis(i + dx[k], j + dy[k]);   //递归求dis(i+dx[k],j+dy[k]),并保存在临时变量temp中   
dis_sk[i][j] = dis_sk[i][j] > temp ? dis_sk[i][j] : temp + 1;    //如果dis_sk[i][j]比temp小,则取temp+1   
}
}
}
return dis_sk[i][j];
}
int main() {
int max_dis = 0;
int temp;
int i, j;
cout << "输入行和列" << endl;
cin >> r >> c;
cout << "输入矩阵元素" << endl;
for (i = 0; i < r; i++)
for (j = 0; j < c; j++) {
cin >> h[i][j];
dis_sk[i][j] = 0;
}

for (i = 0; i < r; i++)
for (j = 0; j < c; j++) {
temp = dis(i, j);
max_dis = max_dis > temp ? max_dis : temp;
}
cout << max_dis + 1 << endl;
system("pause");
return 0;
}

动态规划,写函数,比如这里的int dis(int i, int j) ,你要知道,这里你已经解决了子问题,你所要关心的是存储路径长,也就是这里的int dis_sk[101][101];

老鼠问题需要判断每次尝试是对的还是错的并且记录路径(这里是用1表示)

而动态规划则只记录了每个点可以滑行的最大距离 ,这个矩阵非常重要

可以看出来,这两种算法在运行过程是记录了不同的数据,动态规划是自下而上的,而回溯法则关键在于回溯,回看注释部分,你要记录回溯的地点,那是你开始走错的第一步的位置。

留下一个问题,在最长滑雪路径这个问题的基础上加上打印出该路径(其实就和打印出最长公共子序列问题一样了),细细品味这个问题,可以帮助你更好的理解二者的不同。

一个是过程主义,一个是结果主义。当然过程主义比结果主义强,因为记录了许多细节可以推出结果主义想要的

猜你喜欢

转载自blog.csdn.net/csdnnmbdybb/article/details/78023077