动态规划学习笔记

动态规划

拆分问题,定义问题状态和状态之间的关系,每一状态解决一次,使得问题能够以递推的方式去解决。

动态规划其实感觉和递归想法差不多,就是递归每次每一步都要重复计算,导致复杂度很高,动态规划

就是每一步就计算一次,把这一状态保存下来,下回用的时候直接就用了,很高效。

动态规划解题的一般思路

    1. 将原问题分解为子问题

    把原问题分解为若干个子问题,子问题和原问题形式相同或类似,只不过规模变小了。子问题都解决,原问题即解决(数字三角形例)。

   子问题的解一旦求出就会被保存,所以每个子问题只需求 解一次。

    2.确定状态

   在用动态规划解题时,我们往往将和子问题相关的各个变量的一组取值,称之为一个“状 态”。一个“状态”对应于一个或多个子问题, 所谓某个“状态”下的“值”,就是这个“状 态”所对应的子问题的解。

  所有“状态”的集合,构成问题的“状态空间”。“状态空间”的大小,与用动态规划解决问题的时间复杂度直接相关。 在数字三角形的例子里,一共有N×(N+1)/2个数字,所以这个问题的状态空间里一共就有N×(N+1)/2个状态。

    整个问题的时间复杂度是状态数目乘以计算每个状态所需时间。在数字三角形里每个“状态”只需要经过一次,且在每个状态上作计算所花的时间都是和N无关的常数。

    3.确定一些初始状态(边界状态)的值

    以“数字三角形”为例,初始状态就是底边数字,值就是底边数字值。

   4. 确定状态转移方程

     定义出什么是“状态”,以及在该“状态”下的“值”后,就要找出不同的状态之间如何迁移――即如何从一个或多个“值”已知的 “状态”,求出另一个“状态”的“值”(递推型)。状态的迁移可以用递推公式表示,此递推公式也可被称作“状态转移方程”


主要就是确定初始状态和这个状态转移方程,一般好像都是重后往前推的,初始状态确定也是确定最后的边界,然后重后往前找一个有规律的状态转移方程。

01背包

package com.lq.exercise04;

import java.util.Scanner;

public class Exercise0407 {
	public static void main(String[] args){
	
		Scanner scan=new Scanner(System.in);
		int n=scan.nextInt();
		int m=scan.nextInt();
		System.out.println(n+" "+m);
		int[] w=new int[n+1];
		int[] v=new int[m+1];
		for(int i=1;i<=n;i++){
			w[i]=scan.nextInt();
			v[i]=scan.nextInt();
		}
		int[][] dp=new int[n+1][m+1];
		for(int i=0;i<=m;i++){
			dp[0][i]=0;
		}
		for(int i=0;i<=n;i++){
			dp[i][0]=0;
		}
		for(int i=1;i<=n;i	++){
			for(int j=1;j<=m;j++){
				if(w[i]<=j){
					dp[i][j]=Math.max(dp[i-1][j-w[i]]+v[i],dp[i-1][j]);
				}
				else{
					dp[i][j]=dp[i-1][j];
				}
			}
		}
		System.out.println(dp[n][m]);
	}
}


矩阵相乘最小次数


public class Exercise0404 {
	
	    
	    public static void main(String[] args) {
	
	        Scanner s = new Scanner(System.in);
	        int n = s.nextInt();
	        if(n <= 1 || n > 1000)
	            return;
	        int[] array = new int[n + 1];
	        for(int i = 0;i <= n;i++)
	            array[i] = s.nextInt();
	       printResult(array);
	    }
	 

	public static void printResult(int[] array) {
	        int length = array.length; 
	        long[][] dp = new long[length][length];  
	        long sum;
	        for(int len = 2;len < length;len++) { 
	            for(int i = 1, j = len;j < length;i++, j++) {  
	                long min = Long.MAX_VALUE;
	                for(int k = i;k < j;k++) { 
	                    sum = dp[i][k] + dp[k + 1][j] + array[i - 1] * array[k] * array[j];
	                    if(min > sum)
	                        min = sum;
	                }
	                dp[i][j] = min;
	            }
	        }
	        System.out.println(dp[1][length - 1]);
	        return;
	    }
	}

最大连续子序列和:

给定K个整数的序列{ N1, N2, ..., NK },其任意连续子序列可表示为{ Ni, Ni+1, ..., 
Nj },其中 1 <= i <= j <= K。最大连续子序列是所有连续子序列中元素和最大的一个, 
例如给定序列{ -2, 11, -4, 13, -5, -2 },其最大连续子序列为{ 11, -4, 13 },最大和 
为20。 
在今年的数据结构考卷中,要求编写程序得到最大和,现在增加一个要求,即还需要输出该 
子序列的第一个和最后一个元素。

//sum每次都加,max记录下当前最大的,如果sum是负数,则置为0重新往后开始加,因为sum为负数时再往后加也一定比重新加小,没有可能是最大的,也是动态记录子序列和的状态。

public class Exercise0503 {

	public static void main(String[] args) {
		
		Scanner scan=new Scanner(System.in);
		while(scan.hasNext()){
			int n=scan.nextInt();
			if(n==0){
				break;
			}
			int[] a=new int[n];
			for(int i=0;i<n;i++){
				a[i]=scan.nextInt();
			}
			int start,end,sum,temp,count,max;
			start=end=sum=temp=count=0;
			max=a[0];
			for(int i=0;i<n;i++){
				sum+=a[i];
				if(sum>max){
					System.out.println(temp);
					end=i;
					max=sum;
					start=temp;
					System.out.println(start+" "+temp+" "+end); 
				}else if(sum<0){     //如果是负数肯定不是最大的,再加一个肯定也没有加的那个大,所以重新开始计数。
                    temp=i+1;               
                    sum=0;  
                    count++;              
                }  
            }
            if(count==n){  
                System.out.println("0"+" "+a[0]+" "+a[n-1]);  
            }else{  
                System.out.println(max+" "+a[start]+" "+a[end]);  
            }  
        }  
    }  
}  




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值