递归问题
经典问题:斐波那契数列
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);
}
}