动态规划问题例题总结1

动规概念和本质

动态规划是分治思想的延伸,通俗一点来说就是大事化小,小事化无的艺术。在将大问题化解为小问题的分治过程中,保存对这些小问题已经处理好的结果,并供后面处理更大规模的问题时直接使用这些结果。
动态规划具备了以下三个特点:

  1. 把原来的问题分解成了几个相似的子问题。
  2. 所有的子问题都只需要解决一次。
  3. 储存子问题的解。

动态规划的本质,是对问题状态的定义和状态转移方程的定义(状态以及状态之间的递推关系)动态规划问题一般从以下四个角度考虑:

  1. 状态定义
  2. 状态间的转移方程定义
  3. 状态的初始化
  4. 返回结果

其中状态来源:需要从问题出发,从问题中抽象状态,每一个状态对应一个子问题。那么状态的形式可以有很多种定义,如何验证状态的合理性呢?

  1. 某个状态的解或者多个状态处理之后能对应最终的解
  2. 状态之间可以形成递推关系

在定义时常常需要考虑是一维状态还是二维状态,首先我们应该尝试一维,若合理性不满足,再考虑二维状态,可列举几个常见问题的状态:

  1. 字符串:状态一般对应子串,一般状态每次改变伴随新增一个字符
  2. 矩阵:定义为二维状态,经过优化,大多可变成一维状态。

动规题目详解

1.Fibonacci

大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。n<=39

题解

方法一

fibonacci的定义:F(n) =F(n-1)+F(n-2)。如果此题使用递归的做法,时间复杂度为O(2^n),随着n的增大呈现指数增长,效率低下。当输入比较大时,可能导致栈溢出
在递归过程中有大量的重复计算。

递归代码
class solution{
public:
	int Fibonacci(int n)
	{
		if (n < 0)
		{
			return 0;
		}
		if (n == 1 || n == 2)
		{
			return 1;
		}
		return Fibonacci(n - 1) + (n - 2);
	}
};

方法二

使用动态规划解决。状态:F(n), 递推关系:F(n) =F(n-1)+F(n-2),初始值:F(1)=F(2) = 1,返回值:F(N)。此方法空间复杂度为O(n)。

动规代码
class solution {
public:
	int Fibonacci(int n)
	{
		if (n < 0)
		{
			return 0;
		}
		if (n == 1 || n == 2)
		{
			return 1;
		}
		//开辟一个数组保存从0开始的子问题的解
		int* recond = new int[n + 1];
		recond[0] = 0;
		recond[1] = 1;
		for (int i = 2; i <= n; i++)
		{
			recond[i] = recond[i - 1] + recond[i - 2];
		}
		return recond[n];
		delete[] recond;
	}
};

方法三

通过观察可知,每个F(n)只和F(n-1)和F(n-2)有关,因此可以将代码进行优化,不使用数组,只保存相邻的两个子问题的解即可。此方法空间复杂度为O(1)。

动规优化代码
class solution {
public:
	int Fibonacci(int n)
	{
		if (n < 0)
		{
			return 0;
		}
		if (n == 1 || n == 2)
		{
			return 1;
		}
		int f1 = 1;
		int f2 = 1;
		int result = 0;
		for (int i = 3; i <= n; i++)
		{
			result = f1 + f2;
			f1 = f2;
			f2 = result;
			//更新值
		}
		return result;
	}
};

2.字符串分割

给定一个字符串s和一组单词dict,判断s是否可以用空格分割成一个单词序列,使得单词序列中所有的单词都是dict中的单词(序列可以包含一个或多个单词)。
例如:
给定s=“leetcode”;
dict=[“leet”, “code”].
返回true,因为"leetcode"可以被分割成"leet code".

题解

1.抽象状态:题目可抽象为—字符串s是否可以被分割。2.状态定义:F(i)字符串前i个字符能否被分割。
F(4):前4个字符是否可以被分割,如果可以返回true。
3.状态转移方程:
F(i): j <i && F(j) && substr[j+1,i]能在词典中找到
如题中"leetcode",F(8)可将其分割为
F(0)&&[1,8], //整体为一个单词
F(1)&&[2,8], //前1个字符可否被分割&&[2,8]是否为一个单词
F(2)&&[3,8], //前2个字符可否被分割&&[3,8]是否为一个单词
F(3)&&[4,8],
F(4)&&[5,8],
F(5)&&[6,8],
F(6)&&[7,8],
F(7)&&[8,8]。
此时只需要关心[ ]中的字符能否在字典中找到,因为F( ) 已经在之前的状态中被分割为一个或者多个子串,不需要关心其具体状态,只需要得到true or false的结果。
4.返回结果:F(字符串长度),
5.初始值:对于初始值无法确定的,可以引入一个不代表实际意义的空状态,作为状态的起始空状态的值需要保证状态递推可以正确且顺利的进行,到底取什么值可以通过简单
的例子进行验证F(0) = true。

动规代码
class solution
{
public:
	bool wordBreak(string s, unordered_set<string>& dict)
	{
		if (s.empty())
			return false;
		if (dict.empty())
			return false;
		vector<bool> canbreak(s.size() + 1, false);
		canbreak[0] = true;
		for (int i = 1; i <= s.size(); i++)
		{
			for (int j = i - 1; j >= 0; j--)
			{
				if (canbreak[j] && dict.find(s.substr(j, j - i)) != dict.end())
				{
					canbreak[i] = true;
					break;
				}
			}
		}
		return canbreak[s.size()];
	}
};

三角矩阵

给出一个三角形,计算从三角形顶部到底部的最小路径和,每一步都可以移动到下面一行相邻的数字,
例如,给出的三角形如下:
[↵ [2],↵ [3,4],↵ [6,5,7],↵ [4,1,8,3]↵]
最小的从顶部到底部的路径和是2 + 3 + 5 + 1 = 11。
注意:如果你能只用O(N)的额外的空间来完成这项工作的话,就可以得到附加分,其中N是三角形中的行总数。

题解

因为(i,j)下一步只能走到(i+1,j)或者(i+1,j+1),所以(i-1,j)或者(i-1,j-1)可以到达(i,j)。因此可以正向进行推导,1.子状态:从(0,0)到(1,0),(1,1),(2,0),…(n,n)的最短路径和。2.状态:F(i,j): 从(0,0)到(i,j)的最短路径和。3.状态递推方程:F(i,j) = min( F(i-1, j-1), F(i-1, j)) + triangle[i][j]。4.初始值:F(0,0) = triangle[0][0]。5.返回结果:min(F(n-1, i))。

动规代码
class solution {
public:
	int minimumtotal(vector<vector<int>> &tri)
	{
		if (tri.empty())
			return 0;
		//初始化F
		vector<vector<int>> minsum(tri);
		int line = tri.size();
		for (int i = 1; i < line; i++)
		{
			for (int j = 0; j <= i; j++)
			{
				//处理左右边界
				if (j == 0)
					minsum[i][j] = minsum[i - 1][j];
				else if (j == i)
					minsum[i][j] = minsum[i - 1][j - 1];
				else
					minsum = min(minsum[i - 1][j], minsum[i - 1][j - 1]);
				minsum[i][j] = minsum[i][j] + tri[i][j];
			}
		}
		int result = minsum[line - 1][0];
		for (int i = 0; i < line; i++)
		{
			result = min(minsum[line - 1][i], result);
		}
		return result;
	}
};

猜你喜欢

转载自blog.csdn.net/weixin_40218502/article/details/107765416
今日推荐