动态规划 练习

 

动态规划:

核     心  :重叠子问题,最优子结构

解题要点:递归函数,递归出口

适用范围:最优解,最大值

1.例题引入:斐波那契数列

  使用递归解法:

 public static int Fibonacci(int n) {
        if(n <= 1){
            return n;
        }
        return Fibonacci(n-1)+Fibonacci(n-2);
    }
时间复杂度O(n^2)
时间复杂度O(n^2)

 当n=4时,

Fibonacci(4) = Fibonacci(3) + Fibonacci(2)

​ = Fibonacci(2) + Fibonacci(1) + Fibonacci(1) + Fibonacci(0)

​ = Fibonacci(1) + Fibonacci(0) + Fibonacci(1) + Fibonacci(1) + Fibonacci(0);

重叠子问题:由于我们的代码并没有记录Fibonacci(1)和Fibonacci(0)的结果,对于程序来说它每次递归都是未知的,因此光是n=4时Fibonacci(1)就重复计算了3次之多。

动态规划方法:

	public static int Fibonacci(int m) { //第m项
		int i=m-1;//第m项的下标
		int opt[]=new int[m];  
		opt[0]=1;     //递归出口
		opt[1]=1;
		for(i=2;i<opt.length;i++) {    //递归
			opt[i]=opt[i-1]+opt[i-2];
		}
		return opt[opt.length-1];
	}
时间复杂度:O(n)

2. 加权项目时间计划问题:

问题描述:

在指定的时间内使得收益最高

思路:

递推式:OPT表示最优解
在这里插入图片描述

递归出口: opt[0]=0

代码实现:

	public static int solution(int[] value,int[] prev,int i) {
		int opt[]=new int[i];
		opt[0]=0;
		for(i=1;i<opt.length;i++) {
			int A=value[i]+opt[prev[i]];
			int B=opt[i-1];
			opt[i]=(A>B)?A:B;
		}
		return opt[i-1];
	}

3.例题一:

问题描述:

题目描述:从一组数据中挑出互不相邻的的数,使和最大,挑的数不限量。(例如1,4,7,3 可以,1,8,3不可以)

在这里插入图片描述

解题思路:

该题目中分解的子问题中存在重复项,故用DP效率更高

递推函数:

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

代码实现:

	public static int rec_opt(int i,int[] arr) {  //递归方法
		if(i==0) return arr[i];
		if(i==1) {
			if(arr[0]>arr[1]) return arr[0];
			else return arr[1];
		}
		int A=rec_opt(i-2,arr)+arr[i];
		int B=rec_opt(i-1,arr);
		int result=(A>B)? A:B;
		return result;
	}
	public static int dp_opt(int[] arr) {    //dp方法
		int opt[]=new int[arr.length];   //opt[]用来存储最优解
		opt[0]=arr[0];
		opt[1]=(arr[0]>arr[1])?arr[0]:arr[1];
		for(int i=2;i<arr.length;i++) {
			int A=opt[i-2]+arr[i];
			int B=opt[i-1];
			opt[i]=(A>B)?A:B;
		}
		return opt[opt.length-1];
	}

4.例题二:

问题描述:

给定一个数组arr和数字S,从arr中任意的选取值,使得值的和等于S,如果可以返回True 否则返回False

在这里插入图片描述

解题思路:

分析:建立子集Subset(arr[i],s)其中i表示当前的位置,s表示求的和
例如subset(arr[5],9)就表示

在这里插入图片描述

递推式:

在这里插入图片描述

递归出口:

在这里插入图片描述

DP表:

在这里插入图片描述

代码实现:

#include<stdio.h>
#include<math.h>
int arr[]={3,34,4,12,5,2};
int len=6;
int s=9;
bool dp_subset(int arr[],int s)
{
	bool subset[len][s+1];
	int i,j;
	for(i=0;i<len;i++)  
		for(j=0;j<s+1;j++)
		subset[i][j]=false;
	for(i=0;i<len;i++) //递归出口,第一列全为T
		subset[i][0]=true;
 	for(j=0;j<s+1;j++) //递归出口,第一行全为F
 		subset[0][j]=false;
	for(i=1;i<len;i++)  //从左到右,逐行遍历二维数组
		for(j=1;j<s+1;j++)
		{
			if (arr[i]>s){   //减枝
				subset[i][j]=subset[i-1][j];  
			}else  
            {
                subset[i][j]= (subset[i-1][j]) || (subset[i-1][j-arr[i]]) ;
            }    
		}
	return subset[len-1][s];
}
int main()
{
	
	bool ans=dp_subset(arr,s);
	if(ans){
		printf("True");
	}else{
		printf("False");
	}
	return 0;
} 

5.  01揹包问题(knapsack)

问题描述:

题目描述 小偷带了一个容量为M(20)的包,然后小偷怎么偷上述东西使得价值最高

在这里插入图片描述

解题思路:

递推公式: K也可以理解还能偷几样东西(揹包里还能放几样东西)

递归出口:1.揹包大小为0,或者东西数量为0     2.剪枝:第K件物品太大

代码实现:

#include<stdio.h>
#define N 6
#define M 21

int B[N][M]={0};//保存每种揹包形式的结果 
int v[N]={0,3,4,5,8,10}; 
int w[N]={0,2,3,4,5,9}; 

//对于这种问题 要学好数学的递推公式 然后再抽象成程序语言   最简单的揹包问题的算法 
void knapsack()
{
	int k,c;
	for(k=1;k<N;k++){
		for(c=1;c<M;c++){
			if(w[k]>c)//当前的商品重量大于了揹包容量 
			{
				B[k][c]=B[k-1][c];  //c表示当前揹包的剩余容量 ,k表示还可以偷的个数 
			}else{
				int v1=B[k-1][c-w[k]]+v[k];   //偷的话 
				int v2=B[k-1][c];    //不偷的话
				if(v1>v2){
					B[k][c]=v1;
				}else{
					B[k][c]=v2;
				}				
			}
		}
	}
}
int main()
{
	knapsack(); 
	printf("***%d****\n",B[5][20]);
	return 0; 
} 

6.最长公共子序列(LCS)

问题描述:

给定两个字符串,求解这两个字符串的最长公共子序列(Longest Common Sequence)。

比如字符串1:BDCABA;字符串2:ABCBDAB则这两个字符串的最长公共子序列长度为4,最长公共子序列是:BCBA

解题思路:

LCS问题的递推公式
在这里插入图片描述
c[i,j]表示:(x1,x2…xi) 和 (y1,y2…yj) 的最长公共子序列的长度。(是长度哦,就是一个整数嘛)

代码实现:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1000;
char a[N],b[N];
int dp[N][N];
int main()
{
    int lena,lenb,i,j;
    while(scanf("%s%s",a,b)!=EOF)
    {
        memset(dp,0,sizeof(dp));//设置了递归出口 
        lena=strlen(a);
        lenb=strlen(b);
        for(i=1;i<=lena;i++)
        {
            for(j=1;j<=lenb;j++)
            {
                if(a[i-1]==b[j-1])
                {
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                else
                {
                    dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
        printf("%d\n",dp[lena][lenb]);
    }
    return 0;
}

相关链接:

https://blog.csdn.net/qq_41910103/article/details/94054611#__118

https://www.jianshu.com/p/0044b9774dfd

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章