动态规划(进阶理解)

递归问题

经典问题:斐波那契数列

1、1、2、3、5、8、13、21、34、……
现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。

public class Solution {
    public int Fibonacci(int n) {
        if(n == 0)
            return 0;
        if(n == 1)
            return 1;
        return Fibonacci(n-2) + Fibonacci(n-1);
    }
}

延申题型:母牛的故事

有一头母牛,它每年年初生一头小母牛。每头小母牛从第四个年头开始,每年年初也生一头小母牛。请编程实现在第n年的时候,共有多少头母牛?
输入
输入数据由多个测试实例组成,每个测试实例占一行,包括一个整数n(0<n<55),n的含义如题目中描述。
n=0表示输入数据的结束,不做处理。
输出
对于每个测试实例,输出在第n年的时候母牛的数量。
每个输出占一行。
在这里插入图片描述
规律研究:(说实话这个规律真的难倒我了,后来看了别人的讲解,才发现。别往下看图,你可以自己试着找找规律
在这里插入图片描述

import java.util.Scanner;

public class Main {
    static int sheep(int n){
        if(n<=3)
            return n;
        else return sheep(n-1)+sheep(n-3);//核心
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int sum =0;
        while(scanner.hasNext()) {
            int n = scanner.nextInt();
            if(n == 0)
                break;
            sum = sheep(n);
            System.out.println(sum);
        }
    }
}

动态规划

经典题型:数字三角形

感谢这位兄台,写的真的很好,链接如下:
链接: https://blog.csdn.net/baidu_28312631/article/details/47418773.
在这里插入图片描述
在上面的数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大。路径上的每一步都只能往左下或 右下走。只需要求出这个最大和即可,不必给出具体路径。 三角形的行数大于1小于等于100,数字为 0 - 99
输入格式

5      //表示三角形的行数    接下来输入三角形
7
3   8
8   1   0
2   7   4   4
4   5   2   6   5

要求输出最大和
接下来,我们来分析一下解题思路:
首先,肯定得用二维数组来存放数字三角形
然后我们用D( r, j) 来表示第r行第 j 个数字(r,j从1开始算)
我们用MaxSum(r, j)表示从D(r,j)到底边的各条路径中,最佳路径的数字之和。
因此,此题的最终问题就变成了求 MaxSum(1,1)
当我们看到这个题目的时候,首先想到的就是可以用简单的递归来解题:
D(r, j)出发,下一步只能走D(r+1,j)或者D(r+1, j+1)。故对于N行的三角形,我们可以写出如下的递归式:

    if ( r == N)                
		MaxSum(r,j) = D(r,j)  
	else      
		MaxSum( r, j) = Max{ MaxSum(r+1,j), MaxSum(r+1,j+1) } + D(r,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;  
}      

进一步改进,增加一个二维数组保存每一步的操作。此问题(三角形)的边界为底边,所以首先对底边数据进行保存,再逐层向上递增。
在这里插入图片描述

import java.util.Scanner;

public class Main {
    static int maxapple(int apple[][], int n){
        int i,j;
        /*
         * 开辟一个dp数组,数组有m+1行;n+1列
         * dp[m][n]表示
         */
        int dp[][] = new int[n][n];

        for(j = 0; j < n; j++) {
            dp[n - 1][j] = apple[n - 1][j];
            //System.out.println(dp[n - 1][j]);
        }

        for (i = n-2; i >= 0; i--)
            for(j = 0; j <= i; j++) {
                dp[i][j] = Math.max(dp[i + 1][j], dp[i + 1][j + 1]) + apple[i][j];
                //System.out.println(dp[i][j]);
            }
        return dp[0][0];
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n;
        n = scanner.nextInt();
        int  apple[][] = new int[n][n];
        for (int i = 0; i< n; i++)
            for (int j = 0; j <= i; j++){
                apple[i][j] = scanner.nextInt();
            }
        int sum = maxapple(apple,n);
        System.out.println(sum);
    }
}

延申题型:平安果

简要描述:
给定一个M行N列的矩阵(M*N个格子),每个格子中放着一定数量的平安果。
你从左上角的各自开始,只能向下或者向右走,目的地是右下角的格子。
每走过一个格子,就把格子上的平安果都收集起来。求你最多能收集到多少平安果。
注意:当经过一个格子时,需要一次性把格子里的平安果都拿走。
限制条件:1<N,M<=50;每个格子里的平安果数量是0到1000(包含0和1000).

输入描述:
输入包含两部分:
第一行M, N
接下来M行,包含N个平安果数量
输出描述:
一个整数:最多拿走的平安果的数量

示例:
输入
2 4
1 2 3 40
6 7 8 90
输出
136

import java.util.Scanner;

public class Main {
    static int maxapple(int apple[][], int m, int n){
        int i,j;
        /*
         * 开辟一个dp数组,数组有m+1行;n+1列
         * dp[m][n]表示
         */
        int dp[][] = new int[m+1][n+1];
        dp[0][0] = apple[0][0];

        for (i=1;i<=m;i++)
            dp[i][0] = dp[i - 1][0] + apple[i][0];

        for(j = 1; j<=n; j++)
            dp[0][j] = dp[0][j - 1] + apple[0][j];

        for (i = 1;i <= m; i++)
            for(j=1; j <= n; j++)
                dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]) + apple[i][j];
        return dp[m][n];
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int m,n;
        m = scanner.nextInt();
        n = scanner.nextInt();
        int  apple[][] = new int[m][n];
        for (int i = 0; i< m;i++)
            for (int j = 0; j < n; j++){
                apple[i][j] = scanner.nextInt();
            }
        int i = 1;
        int j = 1;
        int sum = maxapple(apple,m-1,n-1);
        System.out.println(sum);
    }
}

子序列类问题

最长公共子序列

这个问题可以转换为最长公共子序列问题。
如例子中的数组A{5,6, 7, 1, 2, 8},则我们排序该数组得到数组A‘{1, 2, 5, 6, 7, 8},然后找出数组A和A’的最长公共子序列即可。
显然这里最长公共子序列为{5, 6, 7, 8},也就是原数组A最长递增子序列。
思路:
在这里插入图片描述
在这里插入图片描述

最大子序列和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

思路:
用数组d[i] 来保存 当前最大的连续子数组,算法的思想大体是这样的,循环遍历每个数,然后每次检验d[i-1] 是否大于零,只要大于零就 d[i] = d[i-1]+nums[i] ,如果d[i-1]<0 ,那么直接d[i]=nums[i]

算法核心: d[i] = d[i-1]>=0 ? d[i-1]+nums[i] : nums[i]

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n,m;
        n = scanner.nextInt();
        //d 用来保存最大连续子数组的值
        //m 记录d数组中的最大值
        int nums [] = new int[n];
        int d [] = new int[n];

        for(int i=0;i<n;i++)
        {
            nums[i] = scanner.nextInt();
        }
        m = d[0] = nums[0];    // 初始化第一个 d[0]
        for(int i=1;i<n;i++) // 遍历从第二数开始
        {
            if(d[i-1]<0)     // i 之前的最大连续子数组小于零,则抛弃它,自己从开始
                d[i] = nums[i];
            else             // i 之前的最大连续子数组不小于零,则加上他们
                d[i] = nums[i]+d[i-1];
            m = Math.max(d[i],m);  // 保存d[i] 中的最大值
        }
        System.out.println(m);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值