LightOJ 1004
题意:
题目给了一张地图,地图上的数字表示猴子走到这点后能够取得的香蕉数目,猴子只能从自己当前位置向下一行的相邻位置转移,求得到最多的香蕉的数量
这道题跟数字三角形是一个意思的,只是这道题的走的图形不是一个三角形而是菱形,菱形相当于一个正的三角形和一个倒着的三角形拼接而成的。
我们来看菱形的上部分,用dp[i][j]表示在i,j位置得到的最大的香蕉数量.
如果从上往下推:不难发现对于每个边缘的位置,只能从它的上一行的一个位置推下来,比如dp[2][1]位置,只能由dp[1][1]走下来,而dp[3][3]只能由dp[2][2]走下来,而除了dp[i][1]和dp[i][i]这样的边界位置,其余的位置都是由上一行的相邻两个位置推出的,这就涉及到了边界处理
转移方程为:
if(j == 1) dp[i][j] = dp[i - 1][j] + map[i][j];
if (j == i) dp[i][j] = dp[i - 1][j - 1] + map[i][j];
else dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j]) + map[i][j];
如果倒着推:不难发现,对于每一个元素,都是由下一行的两个相邻位置转移过来的, 所以不存在边界处理,我想这就是为啥在做数字三角形的时候,大佬们选择倒推的原因吧。。。
转移方程为:
dp[i][j] = max(dp[i + 1][j], dp[i + 1][j + 1]) + map[i][j];
很不幸,这道题不管是正着推还是倒推,都会涉及边界处理,因为这有一个倒着的三角形~ 这个倒着的三角形处理实际上跟上面讲的一样,不过如果正三角形是正推那倒三角形就倒推,如果正三角形倒推那倒三角形就正推啦~
下面附上AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int T, N;
const int MAXN = 1e2 + 10;
int map[MAXN * 2][MAXN];
int dp[MAXN * 2][MAXN];
void input() { //输入数据
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= i; j++) {
cin >> map[i][j];
}
}
for (int i = N + 1; i <= 2 * N - 1; i++) {
for (int j = 1; j <= 2 * N - i; j++) {
cin >> map[i][j];
}
}
}
int main() {
scanf("%d", &T);
int Case = 0;
while (T--) {
cin >> N;
input();
memset(dp, 0, sizeof dp);
//初始赋值
dp[2 * N - 1][1] = map[2 * N - 1][1];
//我是选择的从最后往前推,倒三角形包含边界处理
for (int i = 2 * N - 2; i >= N; i--) {
for (int j = 1; j <= 2 * N - i; j++) {
if (j == 1) dp[i][j] = dp[i + 1][j] + map[i][j];
else if (j == 2 * N - i) dp[i][j] = dp[i + 1][j - 1] + map[i][j];
else dp[i][j] = max(dp[i + 1][j], dp[i + 1][j - 1]) + map[i][j];
}
}
for (int i = N - 1; i >= 1; i--) {
for (int j = 1; j <= i; j++) {
dp[i][j] = max(dp[i + 1][j], dp[i + 1][j + 1]) + map[i][j];
}
}
cout << "Case " << ++Case << ": ";
cout << dp[1][1] << endl;
}
return 0;
}