赞
踩
动态规划系列专题讲义
专题二:数塔问题
/*
Name:动态规划专题之数塔问题
Author:巧若拙
Description:7625_三角形最佳路径问题
描述:如下所示的由正整数数字构成的三角形:
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,和最大的路径称为最佳路径。你的任务就是求出最佳路径上的数字之和。
注意:路径上的每一步只能从一个数走到下一层上和它最近的下边(正下方)的数或者右边(右下方)的数
输入
第一行为三角形高度100>=h>=1,同时也是最底层边的数字的数目。
从第二行开始,每行为三角形相应行的数字,中间用空格分隔。
输出
最佳路径的长度数值。
样例输入
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
样例输出
30
*/
#include<iostream>
#include<cstring>
using namespace std;
const int MAX = 100;
int map[MAX][MAX];
int B1[MAX][MAX]; //备忘录,记录从位置(x,y)到达底行所获得的最大值
int B2[MAX][MAX]; //动态规划,记录从位置(x,y)到达底行所获得的最大值
int pre[MAX]; //pre[j]相当于B2[i+1][j]
int cur[MAX]; //cur[j]相当于B2[i][j]
int F[MAX];//记录当前行的最优解
int bestP; //记录最优解
void DFS(int n, int x, int y, int curP);//n表示行数,x,y表示当前位置坐标,curP表示目前已走路径上的权值和
int DP_1(int n, int x, int y);//n表示行数,计算从位置(x,y)到达底行所获得的最大值
int DP_2(int n); //动态规划(逆推法)
int DP_3(int n); //动态规划(降维优化1)
int DP_4(int n); //动态规划(降维优化2)
int DP_5(int n); //动态规划(利用原数据)
void PrintPath(int n); //输出最佳路径,需要用到全局变量B2[MAX][]
void PrintPath_2(int n); //输出最佳路径,需要用到全局变量map[MAX][]
int main()
{
int n;
cin >> n;
for (int i=0; i<n; i++)
{
for (int j=0; j<=i; j++)
{
cin >> map[i][j];
}
}
//回溯算法,需要用到全局变量map[MAX][MAX],另有bestP初始化为0
DFS(n, 0, 0, map[0][0]);
cout << bestP << endl;
//记忆化搜索(备忘录算法),需要用到全局变量map[MAX][],另有B1[MAX][]初始化为-1
memset(B1,-1, sizeof(B1)); //先初始化B1的值全为-1
cout << DP_1(n, 0, 0) << endl;
//动态规划(逆推法),需要用到全局变量B2[MAX][]
cout << DP_2(n) << endl;
//动态规划(逆推法),需要用到全局变量pre[]和cur[]
cout << DP_3(n) << endl;
//动态规划(逆推法),需要用到全局变量F[]
cout << DP_4(n) << endl;
PrintPath(n); //输出最佳路径,需要用到全局变量B2[MAX][]
//动态规划(逆推法),需要用到全局变量map[MAX][]
cout << DP_5(n) << endl;
PrintPath_2(n); //输出最佳路径,需要用到全局变量map[MAX][]
return 0;
}
算法1:回溯算法,需要用到全局变量map[MAX][MAX],另有bestP初始化为0。
void DFS(int n, int x, int y, int curP)//n表示行数,x,y表示当前位置坐标,curP表示目前已走路径上的权值和
{
if (x == n-1) //语句1
{
if(curP > bestP)
bestP= //语句2
}
else
{
DFS(n,x+1, y, curP+map[x+1][y]); //向正下方走
DFS(); //语句3
}
}
问题1:能否把语句1改为if (x == n)?为什么?
问题2:将语句2和语句3补充完整。
参考答案:
问题1:不能修改,因为递归出口是x == n-1,因为数组的下标是从0开始的,(n-1)表示第n行(底行)。
问题2:语句2:bestP = curP;
语句3:DFS(n, x+1, y+1,curP+map[x+1][y+1]);
算法2:记忆化搜索(备忘录算法),需要用到全局变量map[MAX][],另有B1[MAX][]初始化为-1。
int DP_1(int n, int x, int y)//n表示行数,计算从位置(x,y)到达底行所获得的最大值
{
if (B1[x][y] != -1)
return //语句1
if(x == n-1)
B1[x][y]= //语句2
else
B1[x][y]= map[x][y] + max(DP_1(n,x+1,y), DP_1(n,x+1,y+1));
return B1[x][y];
}
问题1:将语句1和语句2补充完整。
问题2:与算法1(回溯算法)相比,算法2(备忘录算法)有哪些优越之处?
参考答案:
问题1:语句1:return B1[x][y];
语句2:B1[x][y] = map[x][y];
问题2:回溯算法进行了重复搜索,而备忘录算法利用二维数组B1[x][y]记录了已搜索路径,无需重复搜索,大大提高了效率。
算法3:动态规划(逆推法),需要用到全局变量map[MAX][],另有B2[MAX][]初始化为0。。
int DP_2(int n)
{
for(int j=0; j<n; j++) //语句1
B2[n-1][j]= map[n-1][j];
for (int i=n-2; i>=0; i--) //语句2
{
for (int j=i; j>=0; j--) //语句3
{
B2[i][j] = map[i][j] + max(B2[i+1][j], B2[i+1][j+1]);
}
}
return B2[0][0];
}
问题1:语句1所在循环体的作用是什么?它相当于算法2(备忘录算法)中的哪个语句?
问题2:语句2能否改为:for (int i=0; i<=n-2; i++)?为什么?
问题3:语句3能否改为:for (int j=0; j<=i; j++)?为什么?
参考答案:
问题1:语句1所在循环体的作用是为第n行赋值,它是初始条件,相当于算法2中的语句2(递归出口)。
问题2:不能。因为算法3是从下向上走,最终到达顶点的逆推法,必须逆序扫描行坐标。
问题3:可以。因为算法3的状态方程为:B2[i][j] = map[i][j] +max(B2[i+1][j], B2[i+1][j+1]);是利用下一行元素来计算上一行元素的值,与本行元素无关,故列坐标j递增或递减均可。
算法4:动态规划:使用2个一维数组存储记录,需要用到全局变量map[MAX][], 另有pre[]和cur[]均初始化为0。
int DP_3(int n)
{
for (int j=0; j<n; j++)
pre[j]= map[n-1][j];
for (int i=n-2; i>=0; i--)
{
for (int j=i; j>=0; j--)
{
cur[j] = //语句1
}
for(int j=i; j>=0; j--)
{
pre[j] = //语句2
}
}
return pre[0];
}
问题1:将语句1和语句2补充完整。
问题2:和算法3作比较,DP_3(int n)中的pre[j]和cur[j]分别相当于DP_2(int n)中的哪两个变量?
参考答案:
问题1:语句1:cur[j] = map[i][j] + max(pre[j], pre[j+1]);
语句2:pre[j] = cur[j];
问题2:pre[j]相当于DP_2(int n)中的B2[i+1][j],cur[j]相当于B2[i][j]。
算法5:动态规划:使用1个一维数组存储记录,需要用到全局变量map[MAX][], 另有F[]初始化为0。
int DP_4(int n)
{
for (int j=0; j<n; j++)
F[j]= map[n-1][j];
for (int i=n-2; i>=0; i--)
{
for (int j=0; j<=i; j++) //语句1
{
F[j] = map[i][j] + max(F[j], F[j+1]);
}
}
return //语句2
}
问题1:语句1能否改为:for (int j=i; j>=0; j--) ?为什么?
问题2:将语句2补充完整。
参考答案:
问题1:不能。我们与用二维数组记录结果的算法做比较:
在二维数组算法中B2[i][j] = map[i][j] + max(B2[i+1][j], B2[i+1][j+1]),即第i行第j列的元素,由第i+1行的元素决定,且列坐标j小的元素由j大的元素决定。由于我们记录了B2[i+1][]的值,故在求B2[i][j]时,列坐标j递增或递减均可。
若我们用一维数组F[j]代替B2[i][j],则只记录了列坐标j,未记录行坐标i,在同一行中,必须先求出列坐标j较小的元素,再求j较大的元素。这样先改变的是下标j较小的元素,且其不会影响j大的元素。故在内层循环中,应该让循环变量j的值从小到大递增。
问题2:语句2:return F[0];
算法6:动态规划(逆推法),需要用到全局变量map[MAX][]。
int DP_3(int n)
{
for (int i=n-2; i>=0; i--)
{
for (int j=i; j>=0; j--)
{
map[i][j] += max(map[i+1][j], map[i+1][j+1]);
}
}
return map[0][0];
}
问题1:与算法3(利用B2[MAX][])相比,算法6(利用map [MAX][])有哪些优劣之处?
参考答案:
问题1:算法3是经典的动态规划算法,它利用全局变量B2[MAX][]来记录子问题的解,以空间换时间,提高了效率;算法6的思路和算法3是一样的,它巧妙地利用了原二维数组的空间,直接把元素和作为新的元素值,不需要分配额外的空间,但是因为修改了原始数据,不利于对算法做进一步的降维优化。
拓展练习:
原题只要求算出最佳路径上的数字之和,并未要求输出最佳路径。现在要求:
1. 在算法3 int DP_2(int n)的基础上,编写函数void PrintPath(int n);//输出最佳路径。
2. 在算法6 int DP_5(int n)的基础上,编写函数void PrintPath_2(int n);//输出最佳路径。
参考答案:
void PrintPath(int n) //输出最佳路径
{
intj = 0;
for (int i=0; i<n-1; i++) //输出前n-1行
{
cout<< map[i][j] << "->"; //输出上一行元素
if (B2[i+1][j] < B2[i+1][j+1]) //向右下方走,否则向正下方走
{
j++;
}
}
//输出底层元素
cout << map[n-1][j] << endl;
}
void PrintPath_2(int n) //输出最佳路径
{
int i = 0, j = 0;
for (int k=1; k<n; k++) //输出前n-1行
{
if (map[i+1][j] > map[i+1][j+1]) //向正下方走
{
cout << map[i][j] - map[i+1][j] << "->";
i++;
}
else //向右下方走
{
cout << map[i][j] - map[i+1][j+1] << "->";
i++;
j++;
}
}
//输出底层元素
cout << map[i][j] << endl;
}
课后练习:
练习1:2728_摘花生
描述:Hello Kitty想摘点花生送给她喜欢的米老鼠。她来到一片有网格状道路的矩形花生地(如下图),从西北角进去,东南角出来。
地里每个道路的交叉点上都有种着一株花生苗,上面有若干颗花生,经过一株花生苗就能摘走该它上面所有的花生。Hello Kitty只能向东或向南走,不能向西或向北走。问Hello Kitty 最多能够摘到多少颗花生。
输入
第一行是一个整数T,代表一共有多少组数据。1<=T<= 100
接下来是T组数据。
每组数据的第一行是两个整数,分别代表花生苗的行数R和列数 C ( 1<=R,C <=100),每组数据的接下来R行数据,从北向南依次描述每行花生苗的情况。每行数据有 C 个整数,按从西向东的顺序描述了该行每株花生苗上的花生数目 M (0<= M <= 1000)。
输出
对每组输入数据,输出一行,内容为Hello Kitty能摘到得最多的花生颗数。
样例输入
2
2 2
1 1
3 4
2 3
2 3 4
1 6 5
样例输出
8
16
练习2:7614_最低通行费
描述:一个商人穿过一个N*N 的正方形的网格,去参加一个非常重要的商务活动。他要从网格的左上角进,右下角出。
每穿越中间1个小方格,都要花费1个单位时间。商人必须在(2N-1)个单位时间穿越出去。而在经过中间的每个小方格时,都需要缴纳一定的费用。
这个商人期望在规定时间内用最少费用穿越出去。请问至少需要多少费用?
注意:不能对角穿越各个小方格(即,只能向上下左右四个方向移动且不能离开网格)。
输入
第一行是一个整数,表示正方形的宽度N (1 <= N < 100);
后面 N 行,每行 N 个不大于 100 的整数,为网格上每个小方格的费用。
输出
至少需要的费用。
样例输入
5
1 4 6 8 10
2 5 7 15 17
6 8 9 18 20
10 11 12 19 21
20 23 25 29 33
样例输出
109
提示
样例中,最小值为109=1+2+5+7+9+12+19+21+33。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。