动态规划及其应用

                                       动态规划

         


杭电oj做到了最大子序列和,自己的换了好多方法都超时了,网上一查需要用到动态规划,趁此机会学习一下动态规划
但是感觉动态规划有点像这个递归哈哈哈哈

1.动态规划,就是利用历史记录来避免计算的重复,而这些历史记录我们需要一些变量来保存,一般用到一维数组还有二维数组来保存

2.三个步骤
(1) 定义数组元素的含义,例如你的dp【i】代表的什么含义


(2)找出数组元素之间的关系式,有点类似高中所学的数学归纳法,都是通过已知元素来推未知元素


(3)找出初始值,学过数学归纳法的都知道,虽然我们知道了数组元素之间的关系式,例如 dp[n] = dp[n-1] + dp[n-2],我们可以通过 dp[n-1] 和 dp[n-2] 来计算 dp[n],
但是,我们得知道初始值啊,例如一直推下去的话,会由 dp[3] = dp[2] + dp[1]。而 dp[2] 和 dp[1] 是不能再分解的了,,
所以我们必须要能够直接获得 dp[2] 和 dp[1] 的值而这,就是所谓的初始值。由了初始值,并且有了数组元素之间的关系式
,那么我们就可以得到 dp[n] 的值了,而 dp[n] 的含义是由你来定义的,你想求什么,就定义它是什么,这样,这道题也就解出来了。

 

3.
小青蛙跳台阶


 问题描述,一只青蛙一次可以跳上一级台阶,也可以跳上二级台阶。求台阶跳上一个n级台阶总共有多少种方法。
 
 三大步骤


 一定义dp的含义,我们的问题是跳上n级台阶时的跳法,我们就定义dp【i】的含义是跳上一个n级台阶总共有dp【i】种跳法。
 
 二.找出元素之间的关系式,因为在每一阶台阶时都是从n-1阶还有n-2阶台阶跳上来的
 由于我们要算所有可能的跳法,所以有dp【i】=dp【i-1】+dp【i-2】;
 
 三.找出初始条件
 当 n = 1 时,dp[1] = dp[0] + dp[-1],而我们是数组是不允许下标为负数的,所以对于 dp[1],
 我们必须要直接给出它的数值,相当于初始值,显然,dp[1] = 1。一样,dp[0] = 0.(因为 0 个台阶,那肯定是 0 种跳法了)。于是得出初始值:

 1. dp[0] = 0. dp[1] = 1. | dp[0] = 0. |   |-dp[1] = 1.-| |  n <= 1
    时,dp[n] = n. |  
    
    代码实现:

  int[] dp = new int[n+1];
        // 给出初始值
        dp[0] = 0;
        dp[1] = 1;
        // 通过关系式来计算出 dp[n]
        for(int i = 2; i <= n; i++){
            dp[i] = dp[i-1] + dp[i-2];
        }
    System.out.println(dp[i]);

    
    
 (2)
    例题二,找零钱
   


    已知不同面值的钞票,求如 何用最少数量的钞票组成某个金额,求可 以使用的最少钞票数量。如果任意数量的已知面值钞票都无法组成该金额, 则返回-1。
    输入coins 1 3 5 money 11 输出 3
    
    第一步,确认dp【i】的含义(i元钱时的最少硬币方案)
    第二步,找出元素之间的关系式
    第三步,找出初始值
 

 dp【0】={0}=0
dp【1】={dp【1-arr【1】】+1}=1
dp【2】={dp【2-arr【1】】+1}=2
dp【3】={dp【3-arr【2】】+1}=1
dp【4】={dp【4-arr【2】】+1}=2
    ......
    dp【i】={dp【i-arr【j】(arr[j]<i并且是小于i中最大的面值)】+1}=


 


  代码实现:
        

       int money = sc.nextInt();
            int[] arr = { 1, 3, 5 };
            int[] dp = new int[money + 1];// 每一个都代表一种钱数的拼凑最小值方案
            int max = 0;
            dp[0] = 0;
            for (int i = 1; i < dp.length; i++) {
                for (int j = 0; j < arr.length; j++) {
                    if (arr[j] >= i) {//找到这个临界值,也就是小于i并且是最大的那个面值
                        if (arr[j] == i) {//如果等于i,证明就是一个硬币就可以
                            max = j;//通过索引实现这个arr【j】
                        } else {
                            max = j - 1;
                        }
                        break;//一旦找出这个临界值,立即跳出
                    }
                }
                if (max == -1) {
                    dp[i] = 1;
                } else {
                    dp[i] = dp[i - arr[max]] + 1;
                }

            }
            System.out.println(dp[dp.length - 1]);
        }
    }
}
    

(3)

例题三,找出最短时间(杭电1260)

题目描述:

题意:有K个人买票,可以每个人买单票,给出K个单买时间;也可以和前一个人买双票,给出K-1个买双票时间

第一步,设定dp的含义,因为题目中说是求k个人买票最短时间,所以我们就设dp[k]为k个人买票花费的最短时间

第二步,找出元素之间的关系,在这里我举一个例子

输入:

 单人买票时间 arr[i] 25 20 24 21
相邻双人买票时间  d[i] 40 25 42
dp[0]=0
dp[1]=25
dp[2]=min(25+20,40)
dp[3]=min(min(25+20,40)+24,42+25)
.....

dp[i]=min(dp[i-1]+arr[i],d[i-1]+dp[i-2])

第三步,找出初始值

dp【0】=0;

dp【1】=arr【1】;

第四步,代码实现:


import java.util.*;

public class Main3 {
	// alphabetic order.字母序
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		while (n-- > 0) {
			int k = sc.nextInt();
			int[] arr = new int[k + 1];//单人买票时间
			int d[] = new int[k];
			for (int i = 1; i < arr.length; i++) {
				arr[i] = sc.nextInt();
			}
			if (k == 1) {//k=1的时候直接就输出
				if(arr[1]<10) {
				System.out.println("08:00:0"+arr[1]+" am");
				}else {
					System.out.println("08:00:"+arr[1]+" am");
				}
			} else {
				for (int i = 1; i < d.length; i++) {k!=1的时候就有d【】,输入d【】的值
					d[i] = sc.nextInt();
				}
				int[] dp = new int[k + 1];
				dp[0] = 0;//初始值
				dp[1] = arr[1];//初始值
				for (int i = 2; i < dp.length; i++) {
					dp[i] = Math.min(dp[i - 1] + arr[i], d[i - 1] + dp[i - 2]);//主要的公式
				}
				int hour=dp[k]/3600;
				int min=dp[k]/60%60;
				int s=dp[k]%60;
	                int sumhour = hour + 8;
	                if (sumhour < 10) {
	                    System.out.print("0" + sumhour + ":");
	                } else {
	                    System.out.print(sumhour + ":");
	                }
	                if (min < 10) {
	                    System.out.print("0" + min + ":");
	                } else {
	                    System.out.print(min + ":");
	                }
	                if (s < 10) {
	                    System.out.print("0" + s + " ");
	                } else {
	                    System.out.
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值