C++ 蓝桥杯练习 算法提高 天天向上

题目阅览
 A同学的学习成绩十分不稳定,于是老师对他说:“只要你连续4天成绩有进步,那我就奖励给你一朵小红花。”可是这对于A同学太困难了。于是,老师对他放宽了要求:“只要你有4天成绩是递增的,我就奖励你一朵小红花。”即只要对于第i、j、k、l四天,满足i<j<k<l并且对于成绩wi<wj<wk<wl,那么就可以得到一朵小红花的奖励。现让你求出,A同学可以得到多少朵小红花。
  
输入格式
  第一行一个整数n,表示总共有n天。第二行n个数,表示每天的成绩wi。
  
输出格式
一个数,表示总共可以得到多少朵小红花。

样例输入1
6
1 3 2 3 4 5

样例输出1
6

样例说明
  请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

思路简介
这道题的主要思路是动态规划问题,下面我想先来记录一些关于动态规划的知识,大佬可略过^ _ ^

动态规划思想与性质

首先,动态规划核心思想是把原问题分解成子问题进行求解,也就是分治的思想。

那么什么问题适合用动态规划呢?我们通过一个现实中的例子,来理解这个问题。大家可能在公司里面都有一定的组织架构,可能有高级经理、经理、总监、组长然后才是小开发,今天我们通过这个例子,来讲讲什么问题适合使用动态规划。又到了一年一度的考核季,公司要挑选出三个最优秀的员工。一般高级经理会跟手下的经理说,你去把你们那边最优秀的3个人报给我,经理又跟总监说你把你们那边最优秀的人报给我,经理又跟组长说,你把你们组最优秀的三个人报给我,这个其实就动态规划的思想!
在这里插入图片描述
首先是重叠子问题,不同的问题,可能都要求1个相同问题的解。假如A经理想知道他下面最优秀的人是谁,他必须知道X,Y,Z,O,P组最优秀的人是谁, 甲总监想知道自己下面最优秀的人是谁,也要去知道X,Y,Z组里面最优秀的人是谁?这就有问题重叠了,两个人都需要了解X,Y,Z三个小组最优秀的人。

其次是最优子结构,最优解肯定是有最优的子解转移推导而来,子解必定也是子问题的最优解。甲总监下面最优秀的3个人肯定是从X,Y,Z提交上来的3份名单中选择最优秀的三个人。例如Q哥是X组长下面的第5名,那么他肯定不可能是甲总监下面最优秀的三个。

第三是无后效性,这个问题可能比较难理解,也就是求出来的子问题并不会因为后面求出来的改变。我们可以理解为,X组长挑选出三个人,即便到了高级经理选出大部门最优秀的三个人,对于X组来说,最优秀的还是这3个人,不会发生改变。

求解过程

动态规划问题,大致可以通过以下四部进行解决。

1.划分状态,即划分子问题,例如上面的例子,我们可以认为每个组下面、每个部门、每个中心下面最优秀的3个人,都是全公司最优秀的3个人的子问题

2.状态表示,即如何让计算机理解子问题。上述例子,我们可以实用f[i][3]表示第i个人,他手下最优秀的3个人是谁。

3.状态转移,即父问题是如何由子问题推导出来的。上述例子,每个人大Leader下面最优秀的人等于他下面的小Leader中最优秀的人中最优秀的几个。

4.确定边界,确定初始状态是什么?最小的子问题?最终状态又是什么。例如上述问题,最小的子问题就是每个小组长下面最优秀的人,最终状态是整个企业,初始状态为每个领导下面都没有最优名单,但是小组长下面拥有每个人的评分

以上参考此网址:动态规划算法入门

天天向上实现代码

#include <iostream>
#include <vector>

using namespace std;

long long dp[2001][2001];

int main(){
	int n;
	cin>>n;
	vector<int> numberLists;
	for(int i=0;i<n;i++){
		int temp;
		cin>>temp;
		numberLists.push_back(temp);
	}
	long long  flower = 0;
	for(int i=n-1;i>=0;i--){
 		dp[i][1]=1;
 		for(int j=i+1;j<n;j++){
 		    //如果后边数字大于前面数字
 			if(numberLists[j]>numberLists[i]){
 				int k=2;
 				while(1){
 				    //个数为0时结束循环
 					if(dp[j][k-1]==0) 
					     break;
					 //否则继续循环
 					dp[i][k]=dp[i][k]+dp[j][k-1];
 					k++;
				}		
			}
		}		
	}

    for(int i=n-1;i>=0;i--){
		flower+=dp[i][4];
	}
	cout<<flower<<endl;
	return 0;
} 

代码实现步骤
以下面例子为题介绍:


输入
6
1 3 2 3 4 5

能得到小红花的情况
1 3 4 5
1 2 3 4
1 2 3 5
1 2 4 5
1 3 4 5
2 3 4 5

代码实现思路
首先,我们首先将这些数据存在一个可变数组中,在此使用dp[i][j]表示动态规划,他表示从第i个数开始,所有递增序列长度为j的个数,本题要求四个递增的序列个数,即求dp[i][4]的结果,由此我们便得到小红花的个数。

假设以给定序列 1 3 2 3 4 5为例:
dp[1][2]表示从第二个数3开始,一直到最后的数据5,递增序列长度为2的个数,我们可以观察到,满足这样的序列有2个,后面的34 和 35都符合条件,即。所以dp[1][2]=2;

有了上述的定义,我们就可以得出以下递推公式
dp[i][j]= ∑dp[k][j-1] (k>i,a[k]>a[i])
即接下来就要确定边界条件以及终止条件了,容易知道 dp[n-1][1]是边界条件,值为1,而假设dp[i][j]=0,即没有这样的数据符合要求,即为终止条件。
这样依次计算求出dp[i][4],求和相加得出结果,求解步骤如下所示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
运行截图
在这里插入图片描述

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值