動態規劃 練習

 

動態規劃:

核     心  :重疊子問題,最優子結構

解題要點:遞歸函數,遞歸出口

適用範圍:最優解,最大值

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

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