问题描述
给定高度为n的一个整数三角形,找出从顶部到底部的最小路径和,只能向先移动相邻的结点。首先输入n,接下来的1~n行,第i行输入i个整数,输出分为2行,第一行为最小路径,第2行为最小路径和。
例如,下图是一个n=4的三角形,输出的路径是2 3 5 3,最小路径和是13。
问题求解
将三角形采用二维数组a存放,前面的三角形对应的二维数组如下:
从顶部到底部查找最小路径,那么结点(i,j)的前驱结点只有(i-1,j-1)和(i-1,j)两个:
dp[i][j]表示从顶部a[0][0]查找到(i,j)结点时的最小路径和。
一般情况:
特殊情况:两个边界,即第1列和对角线,达到它们中结点的路径只有一条而不是常规的两条。
状态转移方程如下:
最后求出的最小路径和ans=min(dp[n-1][j])(0≤j<n)。
用pre[i][j]表示查找到(i,j)结点时最小路径上的前驱结点,由于前驱结点只有两个,即(i-1,j-1)和(i-1,j),用pre[i][j]记录前驱结点的列号即可。
在求出ans后,通过pre[n-1][k]反推求出反向路径,最后正向输出该路径。
代码
int a[MAXN][MAXN];
int n;
int ans = 0;
int dp[MAXN][MAXN];
int pre[MAXN][MAXN];//记录列号
int Search()
{
int i, j;
dp[0][0] = a[0][0];
for (i = 1; i < n; i++)
{
dp[i][0] = dp[i - 1][0] + a[i][0];
pre[i][0] = 0;
}
for (i = 1; i < n; i++)
{
dp[i][i] = dp[i - 1][i - 1] + a[i][i];
pre[i][i] = i - 1;
}
for (i = 2; i < n; i++)
{
for (j = 1; j < i; j++)
{
if (dp[i - 1][j - 1] < dp[i - 1][j])
{
pre[i][j] = j - 1;
dp[i][j] = dp[i - 1][j - 1] + a[i][j];
}
else
{
pre[i][j] = j;
dp[i][j] = dp[i - 1][j] + a[i][j];
}
}
}
ans = dp[n - 1][0];
int k = 0;
for (j = 1; j < n; j++)
{
if (ans > dp[n - 1][j])
{
ans = dp[n - 1][j];
k = j;
}
}
return k;//返回最小ans对应的列号
}
void Disppath(int k)
{
int i = n - 1;
vector<int> path;
while (i >= 0)
{
path.push_back(a[i][k]);
k = pre[i][k];
i--;
}
vector<int>::reverse_iterator it;
for (it = path.rbegin(); it != path.rend(); ++it)
cout << *it << " ";
}
算法分析
Search()算法中有i从2到n-1、j从1到i-1的两重循环,容易求出时间复杂度为O(n2)。