动态规划法求解三角形最小路径问题

问题描述

给定高度为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)。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值