算法笔记: 动态规划 求最长子序列的长度

动态规划和递归一样
还是要找一个出口
主要区别是要找一个列表将之间的子问题的答案储存起来

一.寻找最大值

在这里插入图片描述
在这个数组中,不能选相邻的
找出选出数字之和最大的数字

解:
在这里插入图片描述
出口:
在这里插入图片描述
①递归:

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];
}           

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值