Java实现 蓝桥杯 算法提高 天天向上(DP)

首先感谢这两位博主的文章,让我学到了很多知识。

https://blog.csdn.net/baidu_28312631/article/details/47418773

https://blog.csdn.net/a1439775520/article/details/105836763

试题 算法提高 天天向上
问题描述
  A同学的学习成绩十分不稳定,于是老师对他说:“只要你连续4天成绩有进步,那我就奖励给你一朵小红花。”可是这对于A同学太困难了。于是,老师对他放宽了要求:“只要你有4天成绩是递增的,我就奖励你一朵小红花。”即只要对于第i、j、k、l四天,满足i 输入格式
  第一行一个整数n,表示总共有n天。第二行n个数,表示每天的成绩wi。
输出格式
  一个数,表示总共可以得到多少朵小红花。
样例输入
6
1 3 2 3 4 5
样例输出
6
数据规模和约定
  对于40%的数据,n<=50;
  对于100%的数据,n<=2000,0<=wi<=109。

PS:这道题我刚拿到手的时候,最先想到的是暴力求解。这种方法时候在考试中,如果实在想不到或者不太熟悉动态规划,可以先用暴力的方法,使用很短的时间,得到40%的分数。
但是在平常练习中,还是多希望大家可以精益求精。
首先贴出暴力方法,比较简单。

import java.util.Scanner;
public class 天天向上 {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int[] arr = new int[n+1];
		for(int i = 1;i<=n;i++)
		{
			arr[i] = sc.nextInt();
		}
		int cnt = 0;
		for(int i = 1;i<=n;i++)
		{
			for(int j = i;j<=n;j++)
			{
				if(arr[i]<arr[j])
					for(int p = j;p<=n;p++)
					{
						if(arr[j]<arr[p])
							for(int q= p;q<=n;q++)
							{
								if(arr[p]<arr[q])
								{
									cnt++;
								}										
							}
					}
			}
		}
		System.out.println(cnt);
	}

}

接下来我们进入的正题——动态规划
dp[1][i]的意思是每一天都可以做为A同学4天成绩是递增的第一天。所以dp[i][j]保存的状态是第j天可以做为A同学4天成绩递增的第i天的数据。列如:dp[3][5]=4的含义是第5个数(在题上给出的数据是4)可以作为A同学4天中的第3天的次数是4。
以题上的数据为例:1 3 2 3 4 5
1 3 4
1 2 4
1 3 4
2 3 4

import java.util.Scanner;

public class 天天向上_dp {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int[] arr = new int[n+1];
		for(int i = 1;i<=n;i++)
		{
			arr[i] = sc.nextInt();
		}
		int[][] dp = new int[n+1][n+1];
		for(int i = 1;i<=n;i++)
		{
			//初始化dp,含义为每一天都可以作为4天的开始。
			dp[1][i] = 1;
			for(int j = i+1;j<=n;j++)
			{
				if(arr[j]>arr[i])
				{
					//每次循环都会更新第j天的数据
					for(int k = 2;k<=4;k++)
					{
						dp[k][j] += dp[k-1][i];
					}
				}				
			}
		}
		long sum = 0L;
		//dp[4][i]代表的是第i个数可以为第4天的次数
		for(int i = 1;i<=n;i++)
		{
			sum+=dp[4][i];
		}
		System.out.println(sum);
	}

}

动态规划总结:

  1. 将原问题分解为子问题
  • 把原问题分解为若干个子问题,子问题和原问题形式相同或类似,只不过规模变小了。子问题都解决,原问题即解决。(即想要求4天成绩递增,那么就要先求3天成绩递增,想要求三天递增就要先求出连续两天递增…不断把问题的规模变小)
  • 子问题的解一旦求出就会被保存,所以每个子问题只需求 解一次。(dp[i][j]保存的每一个子问题的解)
  1. 确定状态
  • 在用动态规划解题时,我们往往将和子问题相关的各个变量的一组取值,称之为一个“状 态”。一个“状态”对应于一个或多个子问题,所谓某个“状态”下的“值”,就是这个“状 态”所对应的子问题的解。(dp[3][5]=4的状态就是第5天的成绩可以作为连续三天成绩递增的解是4)

  • 所有“状态”的集合,构成问题的“状态空间”。“状态空间”的大小,与用动态规划解决问题的时间复杂度直接相关。

  • 整个问题的时间复杂度是状态数目乘以计算每个状态所需时间。

  1. 确定一些初始状态(边界状态)的值
    天天向上中的初始状态就是,dp[1][i]=1;即每一天都可以作为连续4天递增的第一天。

  2. 确定状态转移方程

    定义出什么是“状态”,以及在该“状态”下的“值”后,就要找出不同的状态之间如何迁移――即如何从一个或多个“值”已知的 “状态”,求出另一个“状态”的“值”(递推型)。状态的迁移可以用递推公式表示,此递推公式也可被称作“状态转移方程”。
    天天向上的递推公式表示为:dp[k][j] += dp[k-1][i];

  3. 能用动规解决的问题的特点

  • 问题具有最优子结构性质。如果问题的最优解所包含的 子问题的解也是最优的,我们就称该问题具有最优子结 构性质。
  • 无后效性。当前的若干个状态值一旦确定,则此后过程的演变就只和这若干个状态的值有关,和之前是采取哪种手段或经过哪条路径演变到当前的这若干个状态,没有关系。

你可能感兴趣的:(Java实现 蓝桥杯 算法提高 天天向上(DP))