前言与重点
1.DP数组
dp[ i ] [ j ]
2.递推公式(状态转移公式)
3.算法步骤
1.确定DP数组及其下标的含义
2.确定递推公式(状态转移公式)
3.dp数组如何初始化
4.确定遍历顺序
5.举例推导dp数组
动态规划理论基础
什么是动态规划
动态规划,英⽂:Dynamic Programming,简称DP,如果某⼀问题有很多重叠⼦问题,使
所以动态规划中每⼀个状态⼀定是由上⼀个状态推导出来的,这⼀点就区分于贪心,贪心没
有状态推导,而是从局部直接选最优的,
(降谷羽:感觉可以称之为空间递推算法)
示例算法:力扣509 斐波那契数列
1.确定dp数组含义
这里dp[i] 的含义为斐波那契数列的第i个数的值
2.确定状态转移方程(递推式)
3.初始化dp数组
4.确定遍历顺序
按依赖关系来确定递推顺序
5.对dp数组递推填充
dp+字符串 经典案例:力扣5 最长回文子串
1.确定dp数组含义
建立二维dp数组 P
p ( i,j ) 表示字符串 sss 的第 i 到 j 个字母组成的子串(下文表示成 s[i:j])是否为回文串
2.确定状态转移方程(递推式)
由于对于一个子串而言,如果它是回文串,并且长度大于 2,那么将它首尾的两个字母去除之后,它仍然是个回文串。例如对于字符串 “ababa”
,如果我们已经知道 “bab” 是回文串,那么 “ababa” 一定是回文串回文串掐头去尾之后,还是回文串!(充分必要)
P (i,j ) = P (i +1,j −1) && ( S[i] == S[j] )
只有 s[i+1:j−1] 是回文串,并且 s 的第 i和 j个字母相同时,s[i:j]才会是回文串。
(不加头尾时是回文串 && 头尾值相等)
3.初始化dp数组
长度为1的一定是回文串
长度为2的,两字符相等就是回文串
vector<vector<int>> dp(n, vector<int>(n)); //创建二维dp数组
// 初始化:所有长度为 1 的子串都是回文串
for (int i = 0; i < n; i++)
{
dp[i][i] = true;
}
4.确定遍历顺序
先从小到大枚举子串长度,再从前往后枚举起始点下标
起始点+长度就可以确定唯一一个子串
// 递推开始
// 先枚举子串长度
for (int L = 2; L <= n; L++)
{
// 枚举左边界,左边界的上限设置可以宽松一些
for (int i = 0; i < n; i++)
{
// 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
int j = L + i - 1;
// 如果右边界越界,就可以退出当前循环
if (j >= n)
{
break;
}
if (s[i] != s[j]) //若头尾不相等
{
dp[i][j] = false;
}
else //若头尾相等
{
if (j - i < 3)
{
dp[i][j] = true;
}
else
{
dp[i][j] = dp[i + 1][j - 1]; //递推式
}
}
//记录最长回文子串 的起始点、长度
if (dp[i][j] && j - i + 1 > maxLen)
{
maxLen = j - i + 1;
begin = i;
}
最后输出答案:
递推到全字符串长度,将结果输出
return s.substr(begin, maxLen);
dp案例:
粉刷匠
#include<bits/stdc++.h>
using namespace std;
using namespace std;
int main()
{
int N, M, T;
cin >> N >> M >> T; // 输入N,M和T
vector<string> boards(N); // 存储木板的向量
for (int i = 0; i < N; i++) {
cin >> boards[i]; // 输入每个木板的颜色信息
}
// 初始化dp数组,dp[i][j]表示前i个木板使用j次粉刷能正确粉刷的最多格子数
vector<vector<int>> dp(N + 1, vector<int>(T + 1, 0));
// 动态规划
for (int i = 1; i <= N; i++)
{
for (int j = 1; j <= T; j++)
{
// 不对第i个木板进行粉刷,直接继承前一个木板的结果
dp[i][j] = dp[i - 1][j];
// 对第i个木板进行粉刷
for (int k = 1; k <= M; k++)
{
if (j >= k)
{
// 计算选择粉刷的格子数
int count_zero = count(boards[i - 1].begin() + k - 1, boards[i - 1].end(), '0');
int count_one = count(boards[i - 1].begin() + k - 1, boards[i - 1].end(), '1');
int count = count_zero + count_one;
// 更新最大的正确粉刷格子数
dp[i][j] = max(dp[i][j], dp[i - 1][j - k] + count);
}
}
}
}
// 输出结果
cout << dp[N][T] << endl;
return 0;
}
浙公网安备 33010602011771号