动态规划和递归一样
还是要找一个出口
主要区别是要找一个列表将之间的子问题的答案储存起来
一.寻找最大值
在这个数组中,不能选相邻的
找出选出数字之和最大的数字
解:
出口:
①递归:
arr = [1, 2, 4, 1, 7, 8, 3]
def rec_opt(arr, i): # 选择第i个
if i == 0: # 只有第0个的话,就是他自己
return arr[0]
elif i == 1: # 第1个的话,找前两个最大的
return max(arr[0], arr[1])
else: # 其他情况分为选或者不选
A = rec_opt(arr, i - 2) + arr[i] # 选第i的话,是他自己的权重 + 与他不相邻且之前的权重最大值
B = rec_opt(arr, i - 1) # 不选的话,就是前一个
return max(A, B) # 在选与不选之间挑一个最大的
rec_opt(arr, 6)
② 动态规划
import numpy as np
arr = [1, 2, 4, 1, 7, 8, 3]
def dp_opt(arr):
opt = np.zeros(len(arr)) # 储存子问题答案数组
# 出口
opt[0] = arr[0]
opt[1] = max(arr[0], arr[1])
# 其他情况
for i in range(2, len(arr)):
A = opt[i-2] + arr[i]
B = opt[i-1]
opt[i] = max(A, B)
return opt[len(arr) - 1]
dp_opt(arr)
二.寻找和式
在一个数组中,我们给定一个数
看能否在这个数组中找到一组式子之和等于给定的这个数字
①递归
arr = [3, 34, 4, 12, 5, 2]
def rec_subset(arr, i , s): # 第i个,给定数字是s
if s == 0: # s是0,肯定能找出一个和式
return True
elif i == 0: # 第0个的话,看arr[0]是否等于s,相等的话就能找出
return arr[0] == s
elif arr[i] > s: # 如果arr[i] > s,继续向后找,即不选这个数
return rec_subset(arr, i-1, s)
else:
A = rec_subset(arr, i-1, s-arr[i]) # 选择这个数,s把选择的数减去
B = rec_subset(arr, i-1, s) # 不选择这个数,s保留
return A or B # 找一个能找出和式的
rec_subset(arr, len(arr)-1, 9)
② 动态规划
在这里我们要定一个二维数组来储存子问题的答案
左边的表示数组中的元素
上边的表示s,因为每次会减,所以s的可能值就是 0~9
如果左边的能组成上边的,就是True
import numpy as np
arr = [3, 34, 4, 12, 5, 2]
def dp_subset(arr, S): # s是给定的数字
subset = np.zeros((len(arr), S + 1), dtype = bool)
subset[:, 0] = True # 0列所有行都是True(都能组成0)
subset[0, :] = False # 0行所有列都是False
subset[0, arr[0]] = True # 但是0行, arr[0]是true
# 填表,最右下角就是答案
for i in range(1, len(arr)):
for s in range(1, S+1):
if arr[i] > s: # 如果当前元素大于给定,不选,往后找
subset[i, s] = subset[i-1, s]
else:
A = subset[i-1, s-arr[i]] # 选
B = subset[i-1, s] # 不选
subset[i,s] = A or B # 有一个是真就行
r, c = subset.shape
return subset[r-1, c-1]
dp_subset(arr, 9)
注:这里的能否找到,看0~i下标的元素能否组成给定数
比如 4 虽然比 3 大,但是从 3 到 4(下标从0到2)的元素中,arr[0]是可以组成3
的,也就是说,我们找的是从arr[0]~arr[i]的元素中能否找到数字组成给定S。
所以我们找最右下角的意思,就是从arr[0]~arr[5]中能否找到组成9的
二.求最长子序列长度
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <cmath>
#define NUM 100
using namespace std;
int son_ans[NUM][NUM];
void LCSLnegth(int x_len, int y_len, const char x[], char y[])
{
int i,j;
// 0行和0列都是0,因为只有一个元素的集合没有真子集
for(i = 1;i <= x_len;i++) son_ans[i][0] = 0;
for(j = 1;j <= y_len;j++) son_ans[0][i] = 0;
// 构造数组son_ans,最后输出右下角的答案
// son_ans[i][j] 表示Xi和Yi的最长公共子序列的长度
for(i = 1;i <= x_len;i++){
for(j = 1;j <= y_len;j++){
if(x[i] == y[j])
son_ans[i][j] = son_ans[i-1][j-1] + 1; // 之前的加上这次的
else if (son_ans[i-1][j] >= son_ans[i][j-1])
son_ans[i][j] = son_ans[i-1][j];
else
son_ans[i][j] = son_ans[i][j-1] ;
}
}
}
int main()
{
int x_len = 7;
int y_len = 6;
char X[x_len] = {'A', 'B', 'C', 'B', 'D', 'A', 'B'};
char Y[y_len] = {'B', 'D', 'C', 'A', 'B', 'A'};
LCSLnegth(x_len, y_len, X, Y);
cout << son_ans[x_len][y_len];
}