DP (1) --- 数塔问题
数塔问题
- 基本模型
如上图所示数塔, 要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少?
数塔思想: 自顶向下分析, 自底向上计算。
H(i) 表示第i层的最大值。要得到H(i + 1), 则考虑上一层结点, 到其相邻节点可取得的值, 取最大值作为H(i + 1)。
如果按照上述做法, 从顶到底算起, 则时间复杂度为 O(2^(n – 1));
考虑从底部算起,计算每个结点到底层的最大值,即上一层结点与其相邻节点的最大值,以这个最大值作为上一层结点的值, 重复这一过程,直到塔顶。时间复杂度为O(n)
- 简单应用
- 多少条路径
猴子爬树, 最初猴子在第一棵树上, 每过一分钟, 猴子会跳到相邻的树上。现在这里有n棵树, 求过了m分钟,有多少种不同的跳法跳到第T棵树。(HDU2151)
考虑以下图形, 是不是很像一个数塔?
- 多顶点型数塔(HDU 1176)
免费馅饼, 有0—10个位置, 起始时, 人在5处, 每一秒都会有几个馅饼落下来, 每秒人可以移动到相邻的位置, 问最多可以接多少个馅饼?
考虑如下算法:
dp[i][j] 表示从第i秒起第j个位置可得到的馅饼;
pie[i][j] 表示第i秒第j个位置落下的馅饼
从最后一秒算起,
dp[i][j] = max(dp[i + 1][j], max(dp[i + 1][j - 1], dp[i + 1][j + 1]))+ pie[i][j]
重复这一过程知道t = 0; 最后这会形成一个多顶点的数塔, 每个位置都是一个顶点。
例题源代码
HDU 2084
#include <stdio.h> int tower[100][100]; int main() { int i, j; int T, N; int v1, v2; scanf ("%d", &T); while (T --) { scanf ("%d", &N); for (i = 1; i <= N; i ++) { for (j = 1; j <= i; j ++) { scanf ("%d", &tower[i][j]); } } for (i = N - 1; i >= 1; i --) // 自底向上计算每一层的最大值 { for (j = 1; j <= i + 1; j ++) { v1 = tower[i + 1][j] + tower[i][j]; v2 = tower[i + 1][j + 1] + tower[i][j]; tower[i][j] = v1; if (v1 < v2) { tower[i][j] = v2; } } } printf ("%d\n", tower[1][1]); } return 0; }
HDU 2151
#include <stdio.h> #include <string.h> int main() { int i, j, k; int numbers; int N, P, M, T; int tree[2][101]; int times[2][101]; while (scanf ("%d%d%d%d", &N, &P, &M, &T) == 4) { memset (times, 0, sizeof (times)); tree[0][0] = P; times[0][P] = 1; numbers = 1; k = 0; while (M --) { j = 0; k %= 2; for (i = 0; i < numbers; i ++) { if (tree[k][i] > 1) { if (times[k ^ 1][tree[k][i] - 1] == 0) { tree[k ^ 1][j ++] = tree[k][i] - 1; } times[k ^ 1][tree[k][i] - 1] += times[k][tree[k][i]]; } if (tree[k][i] < N) { if (times[k ^ 1][tree[k][i] + 1]== 0) { tree[k ^ 1][j ++] = tree[k][i] + 1; } times[k ^ 1][tree[k][i] + 1] += times[k][tree[k][i]]; } times[k][tree[k][i]] = 0; } numbers = j; k ++; } printf ("%d\n", times[k % 2][T]); } return 0; }
HDU 1176
#include <stdio.h> #include <string.h> #define MAX 100002 int pie[MAX][13]; int dp[MAX][13]; int max_v (int a, int b); int main() { int n; int i, j; int t, x; int max_t; while (scanf ("%d", &n) == 1 && n) { memset (pie, 0, sizeof (pie)); memset (dp, 0, sizeof (dp)); max_t = 0; for (i = 0; i < n; i ++) { scanf ("%d%d", &x, &t); if (max_t < t) { max_t = t; } pie[t][x + 1] ++; } for (i = max_t; i >= 0; i --) { for (j = 1; j < 12; j ++) { dp[i][j] = max_v (dp[i + 1][j - 1], max_v (dp[i + 1][j], dp[i + 1][j + 1])) + pie[i][j]; } } printf ("%d\n", dp[0][6]); } return 0; } int max_v (int a, int b) { if (a > b) { return a; } return b; }