DP动态规划

算法设计与分析

Posted by Pele on April 16, 2022

DP 动态规划问题

动态规划流程: 暴力的递归解法 -> 带备忘录的递归解法 -> 迭代的动态规划解法

思考流程:找到状态和选择 -> 明确dp数组/函数的定义 -> 寻找状态之间的关系

斐波那契数列优化

	if (n == 0 || n == 1)
		return n;
	else
	{
		return fib(n - 1) + fib(n - 2);
	}

image-20220326122749664

\(时间复杂度 :O(2^n)\) 带备忘录的递归解法

int fibNote(int n)
{
	if (!n)
		return 0;
	vector<int> mem(n + 1, 0);
	return note(mem, n);
}
// 备忘录📕
int note(vector<int> &mem, int n)
{
	if (n == 1 || n == 2)
		return 1;
	if (mem[n] != 0)
		return mem[n];
	mem[n] = note(mem, n - 1) + note(mem, n - 2);
	return mem[n];
}

时间复杂度$O(n)$

DP数组的迭代解法

if (n <= 0)
		return 0;
	vector<int> fb(n + 1, 0);
	fb[1] = fb[2] = 1;
	for (int i = 3; i < n + 1; i++)
	{
		fb[i] = fb[i - 2] + fb[i - 1];
	}
	return fb[n];

状态转移方程 \(\begin{equation} f(n)=\left\{ \begin{array}{cl} 1&n=1,2\\ f(n-1)+f(n-2)&n>2 \\ \end{array} \right. \end{equation}\)

Fibonacci再优化

if (n <= 0)
		return 0;
	if (n < 3)
		return 1;
	int pre = 1, cur = 1;
	int sum;
	for (int i = 3; i < n + 1; i++)
	{
		sum = pre + cur;
		pre = cur;
		cur = sum;
	}
	return sum;

最长公共子序列

LCS - Longest Common Subsequence

input: str1 = “abcde”, str2 = “ace”

output: 3

  1. Step 1 明确dp数组的含义

    $dp[i][j]$代表$s_1[1…i]$和$s_2[1…j]$的公共子序列

  2. 定义 base case 起始

    $dp[0][…]\&dp[…][0]=0$

  3. 状态转移方程

    用两个指针 i 和 j 从后往前遍历 s1 和 s2 ,如果 s1[i]==s2[j] ,那 么这个字符一定在 lcs ;否则的话, s1[i] 和 s2[j] 这两个字符至少 有一个不在 lcs ,需要丢弃一个。 \(\begin{equation} c(n)=\left\{ \begin{array}{cl} 0 & i>0;j=0\\ c[i-1][j-1]+1&i,j>0;x_i=y_i\\ max\{c[i][j-1],c[i-1][j]\}&i,j>0;x_i!=y_i \end{array} \right. \end{equation}\)