数塔问题
1. 基本模型 (HDU 2084)
如上图所示数塔,要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少?
数塔思想:自顶向下分析,自底向上计算。
H(i) 表示第i层的最大值。要得到H(i + 1),则考虑上一层结点,到其相邻节点可取得的值,取最大值作为H(i + 1)。
如果按照上述做法,从顶到底算起,则时间复杂度为 O(2^(n – 1));
考虑从底部算起,计算每个结点到底层的最大值,即上一层结点与其相邻节点的最大值,以这个最大值作为上一层结点的值,重复这一过程,直到塔顶。时间复杂度为O(n)
2. 简单应用
a. 多少条路径 (HDU 2151)
猴子爬树,最初猴子在第一棵树上,每过一分钟,猴子会跳到相邻的树上。现在这里有n棵树,求过了m分钟,有多少种不同的跳法跳到第T棵树。(HDU2151)考虑以下 图形, 是不是很像一个数塔?
b. 多顶点型数塔(例题 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;最后这会形成一个多顶点的数塔,每个位置都是一个顶点。
3. 例题代码
HDU 2084
#include
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
#include
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
#include
#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;
}