/*测试用例
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
答案:30
*/
方法一:正常递归
简单的分治递归,缺点是重复计算了很多数据,导致运行速度慢,通不过测试。
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
int maxPath(int i, int j, int n); //最大路径函数
int numTri[110][110]; //数字三角形的二维数组
int main()
{
//数字三角形的行数
int n;
cin >> n;
//输入数字三角形
for(int i = 0; i < n; i++)
{
for(int j = 0; j <= i; j++)
{
cin >> numTri[i][j];
}
}
//调用最大路径函数求结果
cout << maxPath(0,0,n) << endl;
return 0;
}
//递归出口是到达最低层时
//没到达出口,就分为两个小三角继续递归
//比较左侧的值和右侧的小三角形谁的路径值大,
//如果左侧小三角形大,就返回左侧值+当前值
//如果右侧小三角形大,就返回右侧值+当前值
int maxPath(int i, int j, int n)
{
//递归出口
if(i == n)
return numTri[i][j];
//求两个小三角形路径的值
int x = maxPath(i+1, j, n);
int y = maxPath(i+1, j+1, n);
//返回路径值比较大的小三角形+当前的值
return max(x, y) + numTri[i][j];
}
方法二:递归 + 备忘录
在递归的基础上,添加了备忘录,将计算过的值记录下来,减少了大量的计算,缩短了运行的时间。缺点是,写备忘录就意味着要用双倍的空间,当组数过大时,就会导致多用了很大空间。(修改的代码注释加了***)
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
int maxPath(int i, int j, int n); //最大路径函数
void Show(int n);//测试用,看备忘录的值
int numTri[110][110]; //数字三角形的二维数组
int Notes[110][110]; //备忘录,没计算过的是-1,计算过的值赋进去**
int main()
{
//数字三角形的行数
int n;
cin >> n;
//输入数字三角形
for(int i = 0; i < n; i++)
{
for(int j = 0; j <= i; j++)
{
cin >> numTri[i][j];
Notes[i][j] = -1; //将备忘录的值赋初值为-1***
}
}
//调用最大路径函数求结果
cout << maxPath(0,0,n) << endl;
Show(n); //测试用,看备忘录的值**
return 0;
}
//递归出口是到达最低层时
//没到达出口,就分为两个小三角继续递归
//比较左侧的值和右侧的小三角形谁的路径值大,
//如果左侧小三角形大,就返回左侧值+当前值
//如果右侧小三角形大,就返回右侧值+当前值***
int maxPath(int i, int j, int n)
{
//递归出口
if(i == n)
{
return numTri[i][j];
}
//如果备忘录的值修改过,就可以直接用**
if(Notes[i][j] != -1)
{
return Notes[i][j];
}
//没修过,就求两个小三角形路径的值
int x = maxPath(i+1, j, n);
int y = maxPath(i+1, j+1, n);
//再加上当前层**
Notes[i][j] = max(x, y) + numTri[i][j];
//返回计算出的备忘录数值**
return Notes[i][j];
}
//测试用,看备忘录的值***
void Show(int n)
{
for(int i = 0; i < n; i++)
{
for(int j = 0; j <= i; j++)
{
cout << Notes[i][j] << ' ';
}
cout << endl;
}
}
/*测试用例
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
答案:30
*/
方法三:状态转移 + 备忘录
根据题意,用状态转移要从最低层开始,最底层最大路径自己是最大的。然后它的上一层的最大路径值 = max(最底层左侧,最底层右侧)+ 上一层那个数的值,以此类推我们得到状态转移方程:
知道原理后,修改代码如下
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
void Show(int n);
int numTri[110][110]; //数字三角形的二维数组
int Notes[110][110]; //备忘录,没计算过的是-1,计算过的值赋进去**
int main()
{
//数字三角形的行数
int n;
cin >> n;
//输入数字三角形
for(int i = 0; i < n; i++)
{
for(int j = 0; j <= i; j++)
{
cin >> numTri[i][j];
}
}
//初始化备忘录, 把最后一行先填进去
for(int j = 0; j < n; j++)
{
Notes[n][j] = numTri[n][j];
}
//除了最后一行的计算方式
for(int i = n-1; i >= 0; i--)
{
for(int j = 0; j <= i; j++)
{
Notes[i][j] = max(Notes[i+1][j],Notes[i+1][j+1]) + numTri[i][j];
}
}
cout << Notes[0][0] << endl;
Show(n);
return 0;
}
//测试用,看备忘录的值***
void Show(int n)
{
for(int i = 0; i < n; i++)
{
for(int j = 0; j <= i; j++)
{
cout << Notes[i][j] << ' ';
}
cout << endl;
}
}
/*测试用例
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
答案:30
*/
缺点和之前的方法一样,备忘录占空间大,于是我们最后采用滚动数组的方式。
方法四:状态转移 + 滚动数组备忘录
我们发现当底层的两个数比较完,与上一层相加之后,底层的前一个数就不需要再使用了。那我们就可以把算出的上一层的值放到底层前一个数的位置,以此类推。这样我们就只需要用一维数组来存储备忘录了。
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
int numTri[110][110]; //数字三角形的二维数组
int Notes[110]; //一维数组备忘录***
int main()
{
//数字三角形的行数
int n;
cin >> n;
//输入数字三角形
for(int i = 0; i < n; i++)
{
for(int j = 0; j <= i; j++)
{
cin >> numTri[i][j];
}
}
//初始化备忘录, 把最后一行先填进去***
for(int j = 0; j < n; j++)
{
Notes[j] = numTri[n][j];
}
//除了最后一行的计算方式
for(int i = n-1; i >= 0; i--)
{
for(int j = 0; j <= i; j++)
{
//修改***
Notes[j] = max(Notes[j],Notes[j+1]) + numTri[i][j];
}
}
//***
cout << Notes[0] << endl;
return 0;
}
/*测试用例
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
答案:30
*/