动态规划
目录
动态规划
求解优化问题的方法
小心+暴力搜索
子问题+复用
递推求解:求fibonacci数列
朴素递归
时间复杂度O(2n)
int Fibonacci1(int n){
int answer;
if(n == 0 || n == 1){
answer = n;
}
else{
answer = Fibonacci(n-1) + Fibonacci(n-2);
}
return answer;
}
朴素递归+记忆化
记忆化:求解过程中每求出一个子问题的解,就记录下来,后续求解过程如果需要该子问题的解,直接查表使用即可
时间复杂度O(n)
const int MAXN = 100;
int memo[MAXN]; //初始化所有元素为-1
int Fibonacci2(int n){
if(memo[n] != -1){
return memo[n];
}
int answer;
if(n == 0 || n == 1){
answer = n;
}
else{
answer = Fibonacci(n-1) + Fibonacci(n-2);
}
memo[n] = answer;
return answer;
}
这就使用动态规划思想,通过记忆化减少重复计算的次数
递推求解
和朴素递归+记忆化原理一样
时间复杂度O(n)
const int MAXN = 100;
int fib[MAXN]; //初始化所有元素为-1
int Fibonacci3(int n){
for(int i = 0; i <= n; ++i){
int answer;
if(i == 0 || i == 1){
answer = i;
}
else{
answer = fib[i-1] + fib[i-2];
}
}
return answer;
}
![image-20220209080534301](https://gitee.com/dctwan/blog-image/raw/master/blogImage/202202090805472.png)
递归+记忆化求解:从右往左求
递推求解:从左往右求
最大连续子序列和
直接考虑该问题的求解比较困难,因为子序列的两端都在动态变化,可以考虑将端固定下来,求以某一个元素作为末尾元素的情况下其最大连续子序列之和,
![image-20220209081450481](https://gitee.com/dctwan/blog-image/raw/master/blogImage/202202090814530.png)
其中Fj是以Aj为结尾的最大连续子序列和
朴素递归
存在大量重复计算,时间复杂度O(n2)
#include<stdio.h>
#include<iostream>
#include<climits>
using namespace std;
#define ll long long
const ll INF = INT_MAX;
const int MAXN = 1E6 + 10;
ll arr[MAXN];
ll fun1(int n){
ll answer;
if(n == 0){
answer = arr[n];
}
else{
answer = max(arr[n], fun1(n-1) + arr[n]);
}
return answer;
}
int main(){
int n;
while(scanf("%d", &n ) != EOF){
for(int i = 0; i < n; ++i){
scanf("%lld", &arr[i]);
}
//遍历arr,求以arr[i]为结尾的子序列和的最大值
ll maximum = -INF;
for(int i = 0; i < n; ++i){
maximum = max(maximum, fun1(i));
}
printf("%lld\n", maximum);
}
return 0;
}
递归+记忆化
消除重复计算,时间复杂度O(n)
ll memo[MAXN]; //初始化为-1
ll fun2(int n){
if(memo[n] != -1){ //之前已经计算过的子问题,直接查表
return memo[n];
}
ll answer;
if(n == 0){
answer = arr[n];
}
else{
answer = max(arr[n], fun2(n-1) + arr[n]);
}
memo[n] = answer; //记录当前子问题的解
return answer;
}
递推求解
时间复杂度O(n)
ll dp[MAXN]; //初始化为-1
void fun3(int n){
for(int i = 0; i < n; ++i){
ll answer;
if(i == 0){
answer = arr[i];
}
else{
answer = max(arr[i], dp[i-1] + arr[i]);
}
dp[i] = answer;
}
return ;
}
最长递增子序列
借用最大连续子序列和问题求解思想,考虑将子序列某一端固定下来,即求以某个元素为末尾元素下的最长递增子序列
![image-20220209155641596](https://gitee.com/dctwan/blog-image/raw/master/blogImage/202202091556705.png)
Fj为以Aj为结尾的最长递增子序列
注:最长递增子序列问题和最大连续子序列和问题的区别在于,后者要求连续,而前者不要求连续。所以,在求Fj时,要考虑以前面所有元素为结尾的最长递增子序列。
朴素递归
存在大量重复计算,时间复杂度O(2n)
#include<stdio.h>
#include<iostream>
using namespace std;
const int MAXN = 1000 + 10;
int arr[MAXN];
int Fun1(int n){
int answer;
if(n == 0){
answer = 1;
}
else{
answer = 1;
for(int i = 0; i < n; ++i){
if(arr[i] < arr[n]){
answer = max(answer, Fun1(i) + 1);
}
}
}
return answer;
}
int main(){
int n;
scanf("%d", &n);
for(int i = 0; i < n; ++i){
scanf("%d", &arr[i]);
}
//recursive
int maxmium = 0;
for(int i = 0; i < n; ++i){
maxmium = max(maxmium, Fun1(i));
}
printf("%d\n", maxmium);
return 0;
}
递归策略+记忆化
memo记录已经计算过的子问题,时间复杂度O(n2)
#include<stdio.h>
#include<iostream>
using namespace std;
const int MAXN = 1000 + 10;
int memo[MAXN]; //初始化为-1
int Fun2(int n){
if(memo[n] != -1){ //子问题如果已经计算过,直接查表
return memo[n];
}
int answer;
if(n == 0){
answer = 1;
}
else{
answer = 1;
for(int i = 0; i < n; ++i){
if(arr[i] < arr[n]){
answer = max(answer, Fun2(i) + 1);
}
}
}
memo[n] = answer; //记录子问题
return answer;
}
int main(){
int n;
scanf("%d", &n);
for(int i = 0; i < n; ++i){
scanf("%d", &arr[i]);
}
fill(memo, memo + n, -1);
int maxmium = 0;
for(int i = 0; i < n; ++i){
maxmium = max(maxmium, Fun2(i));
}
printf("%d\n", maxmium);
return 0;
}
递推求解
时间复杂度O(n2)
#include<stdio.h>
#include<iostream>
using namespace std;
const int MAXN = 1000 + 10;
int arr[MAXN];
int dp[MAXN];
void Fun3(int n){
for(int i = 0; i < n; ++i){
int answer;
if(i == 0){
answer = 1;
}
else{
answer = 1;
for(int j = 0; j < i; ++j){
if(arr[j] < arr[i]){
answer = max(answer, dp[j] + 1);
}
}
}
dp[i] = answer;
}
return ;
}
int main(){
int n;
scanf("%d", &n);
for(int i = 0; i < n; ++i){
scanf("%d", &arr[i]);
}
fill(dp, dp + n, -1);
Fun3(n);
int maxmium = 0;
for(int i = 0; i < n; ++i){
maxmium = max(maxmium, dp[i]);
}
printf("%d\n", maxmium);
return 0;
}
最长公共子序列
对于A1,A2...Ai和B1,B2...Bj
![image-20220209164603166](https://gitee.com/dctwan/blog-image/raw/master/blogImage/202202091646199.png)
其中Fi,j是以Ai和Bj结尾的最长公共子序列长度
朴素递归
时间复杂度O(2n+m)
#include<cstdio>
#include<iostream>
#include<string.h>
using namespace std;
const int MAXN = 1000 + 10;
char str1[MAXN];
char str2[MAXN];
int Fun1(int n, int m){
int answer;
if(n == 0 || m == 0){
answer = 0;
}
else{
if(str1[n] == str2[m]){
answer = Fun1(n-1, m-1) + 1;
}
else{
answer = max(Fun1(n, m-1), Fun1(n-1, m));
}
}
return answer;
}
int main(){
while(scanf("%s%s", str1+1, str2+1) != EOF){
int n = strlen(str1 + 1);
int m = strlen(str2 + 1);
int maxmium = Fun1(n, m);
printf("%d\n", maxmium);
}
return 0;
}
递归策略+记忆化
时间复杂度O(n*m)
#include<cstdio>
#include<iostream>
#include<string.h>
using namespace std;
const int MAXN = 1000 + 10;
char str1[MAXN];
char str2[MAXN];
int memo[MAXN][MAXN]; //记录,初始化均为-1表示该问题尚未被求解
int Fun2(int n, int m){
if(memo[n][m] != -1){ //子问题已经求过,直接查表
return memo[n][m];
}
int answer;
if(n == 0 || m == 0){
answer = 0;
}
else{
if(str1[n] == str2[m]){
answer = Fun2(n-1, m-1) + 1;
}
else{
answer = max(Fun2(n, m-1), Fun2(n-1, m));
}
}
memo[n][m] = answer; //记录子问题的解
return answer;
}
int main(){
while(scanf("%s%s", str1+1, str2+1) != EOF){
int n = strlen(str1 + 1);
int m = strlen(str2 + 1);
for(int i = 0; i <= n; ++i){
for(int j = 0; j <= m; ++j){
memo[i][j] = -1;
}
}
int maxmium = Fun2(n, m);
printf("%d\n", maxmium);
}
return 0;
}
递推求解
时间复杂度O(n*m)
#include<cstdio>
#include<iostream>
#include<string.h>
using namespace std;
const int MAXN = 1000 + 10;
char str1[MAXN];
char str2[MAXN];
int dp[MAXN][MAXN];
void Fun3(int n, int m){
for(int i = 0; i <= n; ++i){
for(int j = 0; j <= m; ++j){
int answer;
if(i == 0 || j == 0){
answer = 0;
}
else{
if(str1[i] == str2[j]){
answer = dp[i-1][j-1] + 1;
}
else{
answer = max(dp[i][j-1], dp[i-1][j]);
}
}
dp[i][j] = answer;
}
}
return;
}
int main(){
while(scanf("%s%s", str1+1, str2+1) != EOF){
int n = strlen(str1 + 1);
int m = strlen(str2 + 1);
Fun3(n, m);
int maxmium = dp[n][m];
printf("%d\n", maxmium);
}
return 0;
}