特点:都是从一个点到另一个点,求路径的最值,而且都有边界条件,例如数字问题从上到下求f[i][j]的最值的时候,需要额外考虑左右边界情况。
例如j=1的时候,下面的数字和只能由上面的第一个推导出来(在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点),因为最左面的节点没有右下角节点,最右面的节点没有左下角节点。
1.蒜头君要回家,已知蒜头君在左下角(1,1)位置,家在右上角(n,n)坐标处。蒜头君走上一-个格子(i, j)会花费- -定的体力aij,而且蒜头君只会往家的方向走,也就是只能往上,或者往右走。蒜头君想知道他回到家需要花费的最少体力是多少。:
例如下图所示,格子中的数字代表走上该格子花费的体力:

对于该图来说,最优策略已在图上标出,最少花费体力为: 3+2 +4+3= 12。
我们把走到一个点看做一个状态,对蒜头君来说,走到一个点只有两种方式,-个是从下面走到该点,-种是从左边走到该点。那么点(i,j)要么是从(i-1,j)走到(i,j),要么是从点(i,j-1)走到(i,j)。
所以从哪个点走到(i,j)就是一个决策。接下来,我们用dp(i,j)来代表走到点(i,j)一共花费的最少体力。
我们需要花费最少力气走到家,所以可以得到状态转移方程: dp(i,j) = min(dp(i-1,j),dp(i,j-1))+ai,j。根据转移方程,我们可以推出走到每个点花费的最少体力。
但是到达边界点的时候无法进行决策 ,例如i==1时 dp[i][j] = dp[i][j - 1] + a[i][j];
j==1时 dp[i][j] = dp[i - 1][j] + a[i][j];
//蒜头君回家
#include<iostream>
#include<algorithm>
using namespace std;
int n;
int main() {
int a[100][100];
int dp[100][100];
cin >> n;
for (int i = 1; i <=n; i++) {
for (int j = 1; j <= n; j++) {
cin >> a[i][j];//输入走每个方格的权值
}
}
dp[1][1] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i == 1 && j == 1) {
dp[i][j] = 0;
}
else if (i == 1) {
dp[i][j] = dp[i][j - 1] + a[i][j];
}
else if (j == 1) {
dp[i][j] = dp[i - 1][j] + a[i][j];
}
else {
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + a[i][j];
}
}
}
cout << "dp[n][n]: " << dp[n][n] << endl;
return 0;
}

2..给定一个如下图所示的数字三角形,从顶部出发,在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点,一直走到底层,要求找出一条路径,使路径上的数字的和最大。
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输入格式
第一行包含整数 n,表示数字三角形的层数。接下来 n 行,每行包含若干整数,其中第 i 行表示数字三角形第 i 层包含的整数。
输出格式
输出一个整数,表示最大的路径数字和。数据范围
1≤n≤500,
−10000≤三角形中的整数≤10000
输入样例:
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输出样例:
30
代码:
#include<iostream>
using namespace std;
int n;
int v[550][550];
int f[550][550];
int ans=-5000000;//因为数据范围是-10000到10000,并且n的取值范围是500,因此极限情况是输入的所有数字都是-10000,最大值也就是50*-10000,因此刚开始定义的ans要小于等于50* -10000.
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++) {
cin >> v[i][j];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++) {
if (j == 1) {
f[i][j] = f[i - 1][j] + v[i][j];
}
else if (j == i) {
f[i][j] = f[i - 1][j - 1] + v[i][j];
}
else
f[i][j] = max(f[i - 1][j - 1]+v[i][j], f[i - 1][j]+v[i][j]);
}
}
for (int i = 1; i <= n; i++) {
ans = max(ans, f[n][i]);
}
cout << ans;
return 0;
}

逆序思考问题:好处,每一个数字的左上角和右上角都有数字,不用考虑边界问题。
#include<iostream>
using namespace std;
int n;
int v[500][500];
int f[500][500];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++) {
cin >> v[i][j];
}
}
for (int i = n; i >=1; i--) {
for (int j = 1; j <= i; j++) {
f[i][j] = max(f[i+1 ][j]+v[i][j], f[i+1 ][j+1]+v[i][j]);
}
}
cout << f[1][1] << endl;
return 0;
}

本文探讨了蒜头君从起点到终点的最短路径问题,以及如何运用动态规划解决数字三角形中路径和的最大化。通过实例和代码展示了如何处理边界条件和决策过程,以及两种不同的思考角度。
2203

被折叠的 条评论
为什么被折叠?



