[USACO1.5][IOI1994]数字三角形 Number Triangles
题目描述
观察下面的数字金字塔。
写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以走到左下方的点也可以到达右下方的点。
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
在上面的样例中,从 7→3→8→7→5 的路径产生了最大
输入格式
第一个行一个正整数 r ,表示行的数目。
后面每行为这个数字金字塔特定行包含的整数。
输出格式
单独的一行,包含那个可能得到的最大的和。
样例 #1
样例输入 #1
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
样例输出 #1
30
提示
【数据范围】
对于 100% 的数据,1≤r≤1000,所有输入在 [0,100] 范围内。
题目翻译来自NOCOW。
USACO Training Section 1.5
IOI1994 Day1T1
法一:暴力
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1010;
int n;
int g[N][N];
int dfs(int x1, int y1){
if(x1 > n || y1 > n) return 0;
//求 最优子问题 dfs(x) = max(dfs(x + 1), dfs(x + 2));
//求 子问题的和 fs(x) = dfs(x + 1) + dfs(x + 2);
else return max(dfs(x1 + 1, y1), dfs(x1 + 1, y1 + 1)) + g[x1][y1];
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++){
for(int j = 1; j <= i; j++){
scanf("%d", &g[i][j]);
}
}
int res = dfs(1, 1);
printf("%d\n", res);
return 0;
}
法二:记忆化搜索
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1010;
int n;
int g[N][N];
int mem[N][N];
int dfs(int x1, int y1){
if(mem[x1][y1]) return mem[x1][y1];
int sum = 0;
if(x1 > n || y1 > n) return 0;
else sum = max(dfs(x1 + 1, y1), dfs(x1 + 1, y1 + 1)) + g[x1][y1];
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++){
for(int j = 1; j <= i; j++){
scanf("%d", &g[i][j]);
}
}
int res = dfs(1, 1);
printf("%d\n", res);
return 0;
}
法三(1):倒序递推(由下至上的递推)
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1010;
int n;
int g[N][N];
int mem[N][N];
int f[N][N];
//int dfs(int x1, int y1){
// if(mem[x1][y1]) return mem[x1][y1];
//
// int sum = 0;
// if(x1 > n || y1 > n) return 0;
// else sum = max(dfs(x1 + 1, y1), dfs(x1 + 1, y1 + 1)) + g[x1][y1];
//}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++){
for(int j = 1; j <= i; j++){
scanf("%d", &g[i][j]);
}
}
for(int i = n; i >= 1; i--){
for(int j = 1; j <= n; j++){
f[i][j] = max(f[i + 1][j], f[i + 1][j + 1]) + g[i][j];
}
}
//int res = dfs(1, 1);
printf("%d\n", f[1][1]);
return 0;
}
法三(2):正序递推
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1010;
int n;
int g[N][N];
int mem[N][N];
int f[N][N];
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++){
for(int j = 1; j <= i; j++){
scanf("%d", &g[i][j]);
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= i; j++){
f[i][j] = max(f[i - 1][j], f[i - 1][j - 1]) + g[i][j];
}
}
int res = 0;
for(int i = 1; i <= n; i++){ //事先不知道终点是那个,要在最后一行里找下
res = max(f[n][i], res);
}
printf("%d\n", res);
return 0;
}
法四:在递推上 将二维 优化到 一维
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1010;
int n;
int g[N][N];
int mem[N][N];
int f[N]; //二维优化到一维
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++){
for(int j = 1; j <= i; j++){
scanf("%d", &g[i][j]);
}
}
for(int i = n; i >= 1; i--){
for(int j = 1; j <= i; j++){
f[j] = max(f[j], f[j + 1]) + g[i][j];
}
}
printf("%d\n", f[1]);
return 0;
}