动态规划入门

首先,我们看一下这道题
输入格式:

5      //表示三角形的行数    接下来输入三角形

7

3   8

8   1   0

2   7   4   4

4   5   2   6   5

在上面的数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大
我们使用D(i,j)来表示第i行的第j个数
MaxNum(i,j)来表示D(i,j)到底边的各路径中的最佳路径(数字之和最大)
返回的值是

###第一版本:普通递归###

#include <iostream>    
#include <algorithm>   
#define MAX 101    
using namespace std;   
int D[MAX][MAX];    
int n;    
int MaxSum(int i, int j){      
    if(i==n)    
        return D[i][j];      
    int x = MaxSum(i+1,j);      
    int y = MaxSum(i+1,j+1);   //递归
    return max(x,y)+D[i][j];    
}  
int main(){      
    int i,j;      
    cin >> n;      
    for(i=1;i<=n;i++){
    	  for(j=1;j<=i;j++){
            cin >> D[i][j]; 
		  }        
	}  
    cout << MaxSum(1,1) << endl;    
}     

Java版递推

public class Main2 {
	
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		
		while (in.hasNext()) {
			int n=in.nextInt();
			int[][] a = new int[n][n];
			for (int i = 0; i < n; i++) {
				for (int j = 0; j <= i; j++) {
					a[i][j] = in.nextInt();
				}
			}
			for (int i = n - 1; i > 0; i--) {
				for (int j = 0; j < i; j++) {
	               a[i-1][j]+=a[i][j]>a[i][j+1]?a[i][j]:a[i][j+1];
				}
			}
			System.out.println(a[0][0]);
		}		
	}
}

###第二版本:记忆型递归###
记忆型递归就是每次计算后(在遍历赋值)给他做个记号,如果已经被标记说明已经计算过,直接返回。

#include <iostream>    
#include <algorithm>   
#define MAX 101    
using namespace std;   
int D[MAX][MAX];    
int n; 
int MaxNum[MAX][MAX];   
int MaxSum(int i, int j){    
    if(MaxNum[i][j]!=-1)
	return MaxSum(i,j);   
    if(i==n)    
        return D[i][j];      
    int x = MaxSum(i+1,j);      
    int y = MaxSum(i+1,j+1);   
    cout << max(x,y)+D[i][j] << endl;     
    return max(x,y)+D[i][j];    
}  
int main(){      
    int i,j;      
    cin >> n;      
    for(i=1;i<=n;i++){
    	  for(j=1;j<=i;j++){
            cin >> D[i][j]; 
            MaxNum[i][j]=-1;
		  }        
	}  
    cout << MaxSum(1,1) << endl;    
}

##动态规划之背包问题
背包问题具体例子:假设现有容量10kg的背包,另外有3个物品,分别为a1,a2,a3. 物品a1重量为3kg,价值为4;
物品a2重量为4kg,价值为5;物品a3重量为5kg,价值为6,将哪些物品放入背包使背包中的价值最大?
动态规划的思路:先将原始问题一般化,欲求总价值,即求前i个物品放入容器m(kg)背包的最大价值c[i][m]–使用一个数组来存储最大价值,当m取10,i取3时,即原始问题了,而前i个物品放入容量为m的背包,又可以转化成前(i-1)个物品放入背包的问题,下面使用数学表达式描述它们两者之间的具体关系
表达式中的各个符号的具体含义
w[i] : 第i个物体的重量
p[i] : 第i个物品的价值
c[i][m] : 前i个物品放入容量m的背包的最大价值
c[i-1][m-w[i]] : 前i-1个物品放入容量为m-w[i]的背包的最大价值
由此可得
c[i][m]=max{c[i-1][m-w[i]]+p[i],c[i-1][m]}


public class DP {
    public static void main(String[] args) {
		int m=10;
		int n=3;
		int[] w={3,4,5};
		int[] p={4,5,6};
		int[][] c=BackPack_Solution(m,n,w,p);
		
		
		
		System.out.println(c[3][10]);
	}
    /**
     * @param m 表示背包的最大容量
     * @param n 表示商品个数
     * @param w 表示商品重量数组
     * @param p 表示商品价值数组
     */
	private static int[][] BackPack_Solution(int m, int n, int[] w, int[] p) {
		int[][] c=new int[n+1][m+1];
		//把二维数组0部分都置0,让从1开始
		for(int i=0;i<n+1;i++){
			c[i][0]=0;
		}
		for(int j=0;j<m+1;j++){
			c[0][j]=0;
		}
		//当物品i件重量为j,如果第i件的重量(w[i-1])小于重量j时,c[i][j]为下列两种情况之一
		//(1)物品i不放入背包,所以c[i][j]等于c[i-1][j]的值
		//(2)物品i放入背包,所以背包剩余的重量为了j-w[i-1],那么c[i][j]等于c[i-1][j-w[i-1]]+p[i-1],
		//注意:w[i-1]和p[i-1]是i物品的重量和价值
		for(int i=1;i<n+1;i++){
			for(int j=1;j<m+1;j++){
				if(w[i-1]<=j){
					if(c[i-1][j]<c[i-1][j-w[i-1]]+p[i-1]){
						c[i][j]=c[i-1][j-w[i-1]]+p[i-1];
					}else{
						c[i][j]=c[i-1][j];
					}
				}else{
					c[i][j]=c[i-1][j];
				}
			}
		}
		
		return c;
	}
}


如果我们有面值为1元、3元和5元的硬币若干枚,如何用最少的硬币凑够11元?

/*
 * 我们要对它抽象一下,
   d(i)=min{ d(i-vj)+1 },其中i-vj >=0,vj表示第j个硬币的面值;
 */

/*
 * 通过追踪我们是如何从前一个状态值得到当前状态值的,可以找到每一次我们用的是什么面值的硬币。
 * 比如,从上面的图我们可以看出,最终结果d(11)=d(10)+1(面值为1),而d(10)=d(5)+1(面值为5),
 * 最后d(5)=d(0)+1 (面值为5)。所以我们凑够11元最少需要的3枚硬币是:1元、5元、5元。
 */
public class Cent {
	public static void main(String[] args) {
       int[] a={1,3,5};
       int sum=6;
       int[] dp=new int[sum+1];
       dp[0]=0;
       for(int i=0;i<=sum;i++)
    	   dp[i]=i;
       for(int i=1;i<=sum;i++){
    	   for(int j=0;j<3;j++){
    		   if(i>=a[j]&&dp[i-a[j]]+1<dp[i]){//对比,去最小值
    			   dp[i]=dp[i-a[j]]+1;
    		   }
    	   }
       }
       System.out.println(dp[sum]);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值