动态规划总结

动态规划是一种运筹学的思想,体现在程序上面就是通过状态转移方程穷举出所有的情况。

状态转移方程的设定思想类似于数学证明上的“数学归纳法”,假设结果是正确的,假设的条件也是正确的,通过假设的条件和结果,推导出结论。说白了,状态转移方程是F(i)=F(i-1)+当前操作;当前的结果,仅仅取决于上一步的结果和本次的决定。

解决动态规划问题中的  dp table(动态规划表) 是一种常用的解法,(并不能保证适用于每一道题)。

dp table 一般有一维数组或者二维数组。要看具体的问题,其实数组的作用就是保存上一次的产生出的结果,方便根据动态转移方程推出当前这一步的结果。

要产生这个dp table ,首先要简化问题,可以先从最简单的例子考虑,设为F(0),根据F(0)求出F(1)以此类推。

举几个例子吧:

链接:https://www.nowcoder.com/questionTerminal/585d46a1447b4064b749f08c2ab9ce66
来源:牛客网
 

对于一个数字序列,请设计一个复杂度为O(nlogn)的算法,返回该序列的最长上升子序列的长度,这里的子序列定义为这样一个序列U1,U2...,其中Ui < Ui+1,且A[Ui] < A[Ui+1]。

给定一个数字序列A及序列的长度n,请返回最长上升子序列的长度。

测试样例:

[2,1,4,3,1,5,6],7
返回:4

忽略复杂度的要求,使用一维数组。

初始化dp数组的值为1 ,下标为0的元素的最长递增子序列就是只有本身1个,F(1)怎么办呢,我们发现,元素4只要在它之前的所有元素中,找到比它本身小的元素,再加上4本身,不就又构成一个新的子序列了吗。

好了我们的转移方程已经找到了,就是

vector<int> dp(A.size(), 1);
for (int i = 0; i < A.size(); ++i)
{
	for (int j = 0; j < i; ++j)
	{
		if (A[j] < A[i])
		{
			dp[i] = max(dp[j] + 1, dp[i]);
		}
	}
}

有了状态转移方程,题就解完了,使用的dp table是一维数组。

class AscentSequence {
public:
	int findLongest(vector<int> A, int n) {
		write code here

			vector<int> dp(A.size(), 1);
		for (int i = 0; i < A.size(); ++i)
		{
			for (int j = 0; j < i; ++j)
			{
				if (A[j] < A[i])
				{
					dp[i] = max(dp[j] + 1, dp[i]);
				}
			}
		}
		int ret = INT_MIN;
		for (auto e : dp)
		{
			ret = max(ret, e);
		}
		return ret;
	}
};

使用dp table是二维数组的例子:https://blog.youkuaiyun.com/weixin_43447989/article/details/103448203?ops_request_misc=%7B"request_id"%3A"158313395919726867851791"%2C"scm"%3A"20140713.130056874.."%7D&request_id=158313395919726867851791&biz_id=0&utm_source=distribute.pc_search_result.none-task

最小路径和:

链接:https://www.nowcoder.com/questionTerminal/2fb62a4500af4f4ba5686c891eaad4a9
来源:牛客网
 

给定一个 n * m 的矩阵 a,从左上角开始每次只能向右或者向下走,最后到达右下角的位置,路径上所有的数字累加起来就是路径和,输出所有的路径中最小的路径和。

 

输入描述:

第一行输入两个整数 n 和 m,表示矩阵的大小。

接下来 n 行每行 m 个整数表示矩阵。

示例1

输入

4 4
1 3 5 9
8 1 3 4
5 0 6 1
8 8 4 0

输出

12

使用二维dp table  只能向左或者向右走,转移方程很好写就是:

上面或者左面的较小值加上本位的值,使用二维数组记录当前值和上一步的值更加方便,空间换时间的做法。

class Solution 
{
public:
	int minPathSum(vector<vector<int> > &grid) 
	{
		//先算出第一行和第一列的特殊的
		int row = grid.size();//行
		int len = grid[0].size();//列
		int i = 0;
		int j = 0;
		vector<vector<int>> arry(row, vector<int>(len, 0));
		arry[0][0] = grid[0][0];
		int tmp=grid[0][0];
		for ( i=1;i<len;++i)
		{
			//行
			tmp= tmp+grid[0][i];
			arry[0][i] = tmp;
		}
		tmp = grid[0][0];
		for ( i=1;i<row;++i)
		{
			//列
			
			tmp = tmp + grid[i][0];
			arry[i][0] = tmp;
		}

		/*for (auto &e : arry[0])
		{
			cout << e << endl;
		}
		for (auto &e : arry[2])
		{
			cout << e << endl;
		}*/
		//取上面和左面的最小值,加上它自己。
		for ( i=1;i<row;++i )
		{
			for (j=1;j<len;++j)
			{
				arry[i][j] = min(arry[i - 1][j], arry[i][j - 1]) + grid[i][j];
			}
		}

		return arry[row - 1][len - 1];
	}
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值