羽猫球比赛题解
可爱的题目捏(虽然也和我有点关系)
根据题目可以了解到,本题需要找一个最长不下降子序列
然后开始看数据范围
10\%
对于
30\%
然后对于
不难发现只需要把所有的子序列全部找出来然后再判断哪一个子序列是最长不下降子序列就好啦!
而此时对于每一位上的数只有两种状态,属于此子序列和不属于此子序列,因此最多也就是有
#include<bits/stdc++.h>\\30标程
using namespace std;
int n;
int a[40];
int bas[40],cnt,ans;
void searchAns(int loc){
if(loc>n){
ans=max(ans,cnt);
return ;
}
if(a[loc]>=bas[cnt]){
bas[++cnt]=a[loc];
searchAns(loc+1);
cnt--;
}
searchAns(loc+1);
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
searchAns(1);
cout<<ans;
return 0;
}
60\%
此时不难发现,
但是
那么问题又来了,这个
等一下,如果我们在遍历完一个数的时候能留下一个和答案有关的信息,后一个数遍历的时候再去查看留下的信息,这样不正好是
那需要留下什么信息呢(?
如果是和答案有关的话,那记录下从第一位到遍历的这位之间的最长不下降子序列如何呢(?
稍加思索发现这样并不好,因为这样会丢失信息与遍历的这个数之间的关系,只留下了信息与位数之间的关系。
所以只需让这个信息代表的是选取这位后的这段区间的最长不下降子序列就好啦
那么我们记录信息的数组就是
这也就是传说中的动态规划
#include<bits/stdc++.h>
using namespace std;
int n;
int a[5200];
int dp[5200];
//dp[从1到i最长不下降子序列(必选i)]
//dp[loc]=max{1+(a[loc]>=a[i]?dp[i]:0)}
int ans;
int main(){
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
dp[1]=1;
for(int loc=2;loc<=n;i++)
for(int i=1;i<loc;i++)
dp[loc]=max(dp[loc],1+(a[i]<=a[loc]?dp[i]:0));
for(int i=1;i<=n;i++)
ans=max(ans,dp[i]);
cout<<ans;
return 0;
}
100\%
我们已经攻克
但是很显然的是,小中野并没有想出来如何不更改dp过程就能优化的
(事实上除了100%的做法其他都是小中野现想出来的)
但是话又说回来,我们是否可以用dp数组的长度来代表答案,dp数组记录第就像Abol变换是将横向求和变成了列向求和一样
这么做的话我们最终只需要输出dp数组的长度就可以了O.O
如果到这里都可以接受的话,那我相信你可以理解,dp数组最终是什么样子我们可能并不关系,我们只关心dp数组的长度。换句话说,我们在进行从第
那么我们考虑一组数据
对于这组数据,一眼
但是我们要怎么转移呢?
很显然如果只有前3项的话,
如果你还记得之前说的,我们只关心dp数组的长度和dp数组的最后一位的话,那么这个问题很好解决,只需要把
同理遍历到原数组第
而对于最后一个数,我们只需要进行更改就可以了, 此时
所以最长不下降子序列长度为
但是要注意最长不下降子序列并不为
说到这里,我们不难写出dp递推方程:如果
观察上式可以看出, 这还是一个
但是你先别急,这个递推式和
这个操作的优化方式已经很显然了,但是这里还是要先按下不表
不知道各位玩没玩过一个游戏: 在
没错,方法就是二分法,本题的这个优化和上边这个小游戏是几乎一模一样的,我们只需要对这个单调的
#include<bits/stdc++.h>
#define itn int
#define lli long long int
#define cs 1000200
using namespace std;
lli n,a[cs],ans;
lli dp[cs];
int main(){
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
dp[ans=1]=a[1];
for(int i=2;i<=n;i++){
if(a[i]>=dp[ans])
dp[++ans]=a[i];
else
dp[upper_bound(dp+1,dp+1+ans,a[i],less<int>())-dp]=a[i];\\upper_bound为C艹内置的二分查抄函数,可以自行百度了解
}
cout<<ans;
return 0;
}