题目如下
上图给出了一个数字三角形。从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,你的任务就是找到最大的和。
路径上的每一步只能从一个数走到下一层和它最近的左边的那个数或者右边的那个数。此外,向左下走的次数与向右下走的次数相差不能超过 1。
输入格式
输入的第一行包含一个整数 0~100,表示三角形的行数。
下面的行给出数字三角形。数字三角形上的数都是0至100之间的整数。
输出格式
输出一个整数,表示答案。
样例输入
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
样例输出
27
题解思路:
非常明显的一道二维dp题,属于是知道我只会二维简单dp,就出了这么一个题让我过(嘿嘿)
自底向上看,因为当前数值 a[i][j] 只能加上他上的数 ans[i - 1][j - 1] 或者 他上面左边的数ans[i - 1][j],由此可得dp公式如下
dp[i][j] = a[i][j] + max(dp[i - 1][j - 1], dp[i - 1][j]);
需要注意的是,本题特此说明:
向左下走的次数与向右下走的次数相差不能超过 1
这也是和普通dp数字三角形不一样的地方, 意味这本道题可以优化
如图所示,由于左右走的次数要么相等,要么差一,所以可得路径必为菱形中的一条,因此可以不必计算其他路径
而当行数为奇数时(假设为5)ans必为dp数组中菱形下角的一个数(本例为2的位置)
而当行数为偶数时(假设为4)ans必为dp数组中菱形下角的两个数中较大值(本例为7 / 4的位置 )
本题收获:
我就像个瞎子一样根本没看到向左下走的次数与向右下走的次数相差不能超过 1
傻fufu的直接交了没剪枝的代码,结果竟然还过了!
后来看其他人题解才发现可以剪枝,但是要难理解一点,多想一点
希望在比赛时不要犯这种低级错误 呜呜呜~
优化后的源码如下:
#include <iostream>
#include <stdlib.h>
#include<string.h>
#include<algorithm>
#include<vector>
typedef long long ll;
using namespace std;
int n;
int a[102][102];
ll ans[102][102];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++) {
cin >> a[i][j];
}
}
// n/2 = 左移次数
// 构造上半三角形
for (int i = 1; i <= n / 2 + 1; i++) {
for (int j = 1; j <= i; j++) {
ans[i][j] = a[i][j] + max(ans[i - 1][j - 1], ans[i - 1][j]);
}
}
// 构造下半三角形
int cnt_i = 0;
for (int i = n/2 + 2; i <= n; i++) {
cnt_i++;
for (int j = 1+cnt_i; j <= n-cnt_i; j++) {
ans[i][j] = a[i][j] + max(ans[i - 1][j - 1], ans[i - 1][j]);
}
}
if (n % 2 == 1) {
cout << ans[n][n / 2 + 1] << endl;
}
else {
cout << max(ans[n][n / 2], ans[n][n / 2 + 1]) << endl;
}
return 0;
}
未优化的源码:
// 用这个替换优化过的构造上半和下半
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++) {
ans[i][j] = a[i][j] + max(ans[i - 1][j - 1], ans[i - 1][j]);
}
}