前言
本题是蓝桥杯算法训练中经典题目,主要考察动态规划这个知识点,另外在网上大多数人的做法是将这道题看做寻找满足条件的可行路问题来求解的。因此我想用图文结合的方式讲述这道题。一、问题描述
A同学的学习成绩十分不稳定,于是老师对他说:“只要你连续4天成绩有进步,那我就奖励给你一朵小红花。”可是这对于A同学太困难了。于是,老师对他放宽了要求:“只要你有4天成绩是递增的,我就奖励你一朵小红花。”即只要对于第i、j、k、l四天,满足i<j<k<l并且对于成绩wi<wj<wk<wl,那么就可以得到一朵小红花的奖励。现让你求出,A同学可以得到多少朵小红花。
输入格式
第一行一个整数n,表示总共有n天。第二行n个数,表示每天的成绩wi。
输出格式
一个数,表示总共可以得到多少朵小红花。
样例输入
6
1 3 2 3 4 5
样例输出
6
数据规模和约定
对于40%的数据,n<=50;
对于100%的数据,n<=2000,0<=wi<=109。
二、问题解析
1.思路
在解这道题时,我们依然用上述样例作为例子,我们可以观察到上述例子,满足四天成绩递增的有第1、3、4、5天,有第1、3、4、6天,有第1、3、5、6天,有第1、2、5、6天,有第1、4、5、6天和第3、4、5、6天。总共6种排列方式。这道题的思路是每一天都要做起点,看是否有满足条件的路,另外题目要求递增,我们可以两两进行比较得出结果。在这里我们可以从第六天往第一天推,也可以第一天往第六天推。因为题目要求的是随时间递增,也就是说满足要求的四天中,后一天成绩总是比前一天的高。我们可以想成是寻找长度为4的路,先选择一个天数为起点,其本身长度为1。如果两天满足递增关系则长度为2,三天满足则长度为3,最后找到长度4的路就是满足要求的四天成绩天天向上。统计这些满足要求的可行路,最后得到输出答案。
2.具体实现
在这里我们用第六天往第一天推的方式,这里用代码可以这样表示:
for(int i=day-1;i>=0;i--)
{
for(int z=i+1;z<day;z++)
if(list[z]>list[i])
{
arr[i][2]=arr[z][1]+arr[i][2];
arr[i][3]=arr[z][2]+arr[i][3];
arr[i][4]=arr[z][3]+arr[i][4];
}
}
用图可以表示为依次以6、5、4、3、2、1为起点,往后面的天数寻找满足递增条件的路。
先确定第六天的可行路,由于第六天是最后一天,所以不存在以第六天为起点往后的路。然后第五天的路,通过比较第五天成绩和第六天成绩可以得到第五天到第六天有一条长度为2的路,在代码上可以表示为:
arr[4][2]=arr[5][1]+arr[4][2];
而第四天的可行路,先将第四天设为起点,然后后面第5、6天作比较看是否有符合条件的路,由于第四天成绩为3,因此可以找到符合条件的路,但4、5、6最多才3天,因此还没有找到长度为4的路。其代码表示为:
arr[3][2]=arr[4][1]+arr[3][2]; arr[3][2]=arr[5][1]+arr[3][2];
arr[3][3]=arr[4][2]+arr[3][3];
而第三天的可行路,先将第3天设为起点,然后后面第4、5、6天作比较看是否有符合条件的路:
arr[2][2]=arr[3][1]+arr[2][2];arr[2][2]=arr[4][1]+arr[2][2]; arr[2][2]=arr[5][1]+arr[2][2];
arr[2][3]=arr[3][2]+arr[2][3];arr[2][3]=arr[4][2]+arr[2][3];
arr[2][4]=arr[3][3]+arr[2][4];
而第二天的可行路,先将第2天设为起点,然后后面第3、4、5、6天作比较看是否有符合条件的路:
arr[1][2]=arr[4][1]+arr[1][2]; arr[1][2]=arr[5][1]+arr[1][2];
arr[1][3]=arr[4][2]+arr[1][3];
而第一天的可行路,先将第1天设为起点,然后后面第2、3、4、5、6天作比较看是否有符合条件的路:
arr[0][2]=arr[1][1]+arr[0][2]; arr[0][2]=arr[2][1]+arr[0][2]; arr[1][2]=arr[3][1]+arr[0][2]];arr[0][2]=arr[4][1]+arr[0][2]; arr[0][2]=arr[5][1]+arr[0][2];
arr[0][3]=arr[1][2]+arr[0][3];arr[0][3]=arr[2][2]+arr[0][3];arr[0][3]=arr[3][2]+arr[0][3];arr[0][3]=arr[4][2]+arr[1][3];
arr[0][4]=arr[1][3]+arr[0][4];arr[0][4]=arr[2][3]+arr[0][4];arr[0][4]=arr[3][3]+arr[0][4];
如果是从第一天往第六天推,是一样的道理,可以修改代码为:
for(int i=0;i<day;i++)
{
for(int z=i-1;z>=0;z--)
if(list[z]<list[i])
{
arr[i][2]=arr[z][1]+arr[i][2];
arr[i][3]=arr[z][2]+arr[i][3];
arr[i][4]=arr[z][3]+arr[i][4];
}
}
3.整体代码
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int day;
long long res=0;
long long arr[2000][5]={0};
vector<int> list;
int count=0;
cin>>day;
for(int i=0;i<day;i++)
{
int temp;
cin>>temp;
list.push_back(temp);
arr[i][1]=1;
}
for(int i=day-1;i>=0;i--)
{
for(int z=i+1;z<day;z++)
if(list[z]>list[i])
{
arr[i][2]=arr[z][1]+arr[i][2];
arr[i][3]=arr[z][2]+arr[i][3];
arr[i][4]=arr[z][3]+arr[i][4];
}
}
for(int i=0;i<day;i++)
{
res=arr[i][4]+res;
}
cout<<res<<endl;
return 0;
}