链接
数塔 - http://acm.hdu.edu.cn/showproblem.php?pid=2084
分析1 —— 自顶而下
- 路径:共有 2 n − 1 2^{n-1} 2n−1条路径。因为每一条路径都会一分为二,且初始时刻有1条路径。
- 状态
- 每一次位置变化都是一个状态,这些状态反映了之前的情况,且是后续的基础
- 2 0 ⏞ S : 初 态 + 2 1 + 2 2 + 2 3 + … … + 2 n − 2 ⏞ 中 间 状 态 + 2 n − 1 ⏞ T : 末 态 \overbrace{2^0}^{S:初态} +\overbrace{2^1 + 2^2 + 2^3 + ……+ 2^{n-2}}^{中间状态}+ \overbrace{2^{n-1}}^{T:末态} 20 S:初态+21+22+23+……+2n−2 中间状态+2n−1 T:末态
- {9}是初态;{9,12,6}、{9,15,6,18}是中间态;{9,15,6,9,10}、{9,12,6,9,10}是末状态
- 求解目标是寻找最大和的末态
- 无法用一个有效的贪心策略事先推断出哪一条路径最优
- 求解策略:分类贪心;走一步看一步,动态决策
- 递推1 —— 从初态开始,考虑:到哪里去?
- DFS,主要考虑:一个对一个的影响,比较次数多,但存在大量的无效比较
- 子结构 —— 状态分类
- 最优子结构 —— 分类贪心
- BFS,主要考虑:一类对一类的影响,由于利用最优子结构避免了无效比较,因此比较次数少
- DFS,主要考虑:一个对一个的影响,比较次数多,但存在大量的无效比较
- 状态设计
- 按最后一步所在的位置分类,每一类为一个状态,共15个状态;求解的过程就是逐步求得每一类的最优解
- 初始状态(1,1)
- 末状态(5,1)、(5,2)、(5,3)、(5,4)、(5,5)
- 状态转移方程:
dp(i,j)
→ \rightarrow →dp(i+1,j)
和dp(i+1,j+1)
dp(i+1, j) = max(dp(i, j), dp(i, j) + d(i+1, j));
dp(i+1, j+1) = max(dp(i+1, j+1), d(i,j) + d(i+1, j+1));
- 递推过程 —— 从第一个已知的位置开始
( 1 , 1 ) → {(1,1)}\rightarrow (1,1)→ ( 2 , 1 ) 、 ( 2 , 2 ) → {(2,1)、(2,2)}\rightarrow (2,1)、(2,2)→ ( 3 , 1 ) 、 ( 3 , 2 ) 、 ( 3 , 3 ) → {(3,1)、(3,2)、(3,3)}\rightarrow (3,1)、(3,2)、(3,3)→ ( 4 , 1 ) 、 ( 4 , 2 ) 、 ( 4 , 3 ) 、 ( 4 , 4 ) → {(4,1)、(4,2)、(4,3)、(4,4)}\rightarrow (4,1)、(4,2)、(4,3)、(4,4)→ ( 5 , 1 ) 、 ( 5 , 2 ) 、 ( 5 , 3 ) 、 ( 5 , 4 ) 、 ( 5 , 5 ) \bcancel{(5,1)、(5,2)、(5,3)、(5,4)、(5,5)} (5,1)、(5,2)、(5,3)、(5,4)、(5,5) - 状态表的变化
列0 | 列1 | 列2 | 列3 | 列4 | 列5 |
---|---|---|---|---|---|
行0 | 0 | 0 | 0 | 0 | 0 |
行1 | 0 9 \bcancel{0} \quad 9 0 9 | 0 | 0 | 0 | 0 |
行2 | 0 21 \bcancel{0} \quad 21 0 21 | 0 24 \bcancel{0} \quad 24 0 24 | 0 | 0 | 0 |
行3 | 0 31 \bcancel{0} \quad 31 0 31 | 0 27 30 \bcancel{0} \quad \bcancel{27} \quad 30 0 27 30 | 0 32 \bcancel{0} \quad 32 0 32 | 0 | 0 |
行4 | 0 33 \bcancel{0} \quad 33 0 33 | 0 49 48 \bcancel{0} \quad 49 \quad \bcancel{48} 0 4948 | 0 39 41 \bcancel{0} \quad \bcancel{39} \quad 41 0 39 41 | 0 37 \bcancel{0} \quad 37 0 37 | 0 |
行5 | 0 52 \bcancel{0} \quad 52 0 52 | 0 40 56 \bcancel{0} \quad \bcancel{40} \quad 56 0 40 56 | 0 59 51 \bcancel{0} \quad 59 \quad \bcancel{51} 0 5951 | 0 45 41 \bcancel{0} \quad 45 \quad \bcancel{41} 0 4541 | 0 53 \bcancel{0} \quad 53 0 53 |
- 最终状态
- 递推2 —— 从末态开始,考虑:从哪里来?
- 递推过程基本不变
- 状态转移方程:
dp(i-1, j-1)、 dp(i-1, j)
→ \rightarrow →dp(i,j)
dp(i, j) = max(dp(i-1, j-1), d(i-1, j))+d(i,j);
- 从第一个未知位置开始求解
( 1 , 1 ) → \bcancel{(1,1)}\rightarrow (1,1) → ( 2 , 1 ) 、 ( 2 , 2 ) → {(2,1)、(2,2)}\rightarrow (2,1)、(2,2)→ ( 3 , 1 ) 、 ( 3 , 2 ) 、 ( 3 , 3 ) → {(3,1)、(3,2)、(3,3)}\rightarrow (3,1)、(3,2)、(3,3)→ ( 4 , 1 ) 、 ( 4 , 2 ) 、 ( 4 , 3 ) 、 ( 4 , 4 ) → {(4,1)、(4,2)、(4,3)、(4,4)}\rightarrow (4,1)、(4,2)、(4,3)、(4,4)→ ( 5 , 1 ) 、 ( 5 , 2 ) 、 ( 5 , 3 ) 、 ( 5 , 4 ) 、 ( 5 , 5 ) {(5,1)、(5,2)、(5,3)、(5,4)、(5,5)} (5,1)、(5,2)、(5,3)、(5,4)、(5,5)
分析2 —— 自底而上 (路径可逆)
代码
自顶而下
- 到哪里去?
/* hdu 2084 数塔 自顶而下 到哪里去?*/
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define MXN 110
int n, d[MXN][MXN], dp[MXN][MXN];
int main(){
int T;
cin >> T;
while(T--){
scanf("%d", &n);
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= i; ++j){
scanf("%d", d[i]+j);
}
}
memset(dp, 0, sizeof dp);
dp[1][1] = d[1][1];
for(int i = 1; i < n; ++i) {
for(int j = 1; j <= i; ++j){ // 从已知出发
dp[i+1][j] = max(dp[i+1][j], dp[i][j]+d[i+1][j]);
dp[i+1][j+1] = max(dp[i+1][j+1], dp[i][j]+d[i+1][j+1]);
}
}
for(int i = 1; i <= n; i++)
if(dp[n][0] < dp[n][i]) dp[n][0] = dp[n][i];
printf("%d\n", dp[n][0]);
}
return 0;
}
- 从哪里来?
/* hdu 2084 数塔 自顶而下 从哪里来?*/
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define MXN 110
int n, d[MXN][MXN], dp[MXN][MXN];
int main(){
int T;
cin >> T;
while(T--){
scanf("%d", &n);
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= i; ++j){
scanf("%d", d[i]+j);
}
}
memset(dp, 0, sizeof dp);
dp[1][1] = d[1][1];
for(int i = 2; i <= n; ++i) {
for(int j = 1; j <= i; ++j){
dp[i][j] = max(dp[i-1][j-1], dp[i-1][j])+d[i][j];
}
}
for(int i = 1; i <= n; i++)
if(dp[n][0] < dp[n][i]) dp[n][0] = dp[n][i];
printf("%d\n", dp[n][0]);
}
return 0;
}
自底而上
- 从哪里来?
/* hdu 2084 数塔 自底而上 从哪里来?*/
/* 空间优化 */
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define MXN 110
int n, dp[MXN][MXN];
int main(){
int T;
cin >> T;
while(T--){
scanf("%d", &n);
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= i; ++j){
scanf("%d", dp[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]);
}
}
printf("%d\n", dp[1][1]);
}
return 0;
}
- 到哪里去?
/* hdu 2084 数塔 自底而上 到哪里去?*/
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define MXN 110
int n, d[MXN][MXN], dp[MXN][MXN];
int main(){
int T;
cin >> T;
while(T--){
scanf("%d", &n);
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= i; ++j){
scanf("%d", d[i]+j);
}
}
memset(dp, 0, sizeof dp);
for(int i = 1; i <= n; ++i) dp[n][i] = d[n][i];
for(int i = n; i > 1; --i) { // 从已知出发
for(int j = 1; j <= i; ++j){
dp[i-1][j] = max(dp[i-1][j], dp[i][j]+d[i-1][j]);
dp[i-1][j-1] = max(dp[i-1][j-1], dp[i][j]+d[i-1][j-1]);
}
}
printf("%d\n", dp[1][1]);
}
return 0;
}