求序列最长不下降子序列_求最长不下降子序列++

【题目描述】

设有由n(1≤n≤200)n(1≤n≤200)个不相同的整数组成的数列,记为:b(1)、b(2)、……、b(n)b(1)、b(2)、……、b(n)若存在i1

例如13,7,9,16,38,24,37,18,44,19,21,22,63,15。例中13,16,18,19,21,22,63就是一个长度为7的不下降序列,同时也有7 ,9,16,18,19,21,22,63组成的长度为8的不下降序列。

【输入】

第一行为n,第二行为用空格隔开的n个整数。

【输出】

第一行为输出最大个数max(形式见样例);

第二行为max个整数形成的不下降序列,答案可能不唯一,输出一种就可以了,本题进行特殊评测。

【输入样例】

14

13 7 9 16 38 24 37 18 44 19 21 22 63 15

【输出样例】

max=8

7 9 16 18 19 21 22 63

再加上一问,求当有多少种方案得到最长不下降子序列

思路:单纯的求最长不下降子序列的长度再熟悉不过,不必多说。关键是那两问如何解决?

那就是另外开两个数组。首先,输出一种方案,开一个pre数组,用来记录前面的轨迹。比如说a[j]+1>l,那么更新,pre[i]=j(a[i]前面的数是a[j]),最后找到其中一个最大值,输出即可,详情见代码

其次,求方案数,开一个g数组。如果l>f[i],那么说明到a[i]是要经过a[j]的,所以g[i]=g[j],同时更新,f[i]=l。如果l==f[i](也有可能是由l>f[i]得来的,也就是说如果l>a[i],要跑两个if),根据加法原理,两个都行,那么就相加,也就是g[i]+=g[j]

由以上思路,简单写一下核心代码:

for(int i=1;i<=n;i++)

{

f[i]=1;g[i]=1;pre[i]=0;//因为一个数本身算一个,所以f数组当然初始化为1.而选这个数肯定也至少有一种方案,因为无论如何都可以选。把pre数组初始化是为了在输出的时候用while

for(int j=1;j

{

if(a[j]<=a[i])

{

int l=f[j]+1;

if(l>a[i])

{

g[i]=0;//这里跟思路不太一样,让g[i]=0,因为当它等于0时,下一个if相加就相当于g[i]=g[j],如果将两个if倒过来,应该可以写成g[i]=g[j]

a[i]=l;

pre[i]=j;

}

if(l==a[i])

{

g[i]+=g[j];

}

}

最后的话只要循环一遍f数组,找到最大的其中那个值,另开一个数组,因为pre数组是倒着的,比如说最大的那个值是第i位,那么用一个while,就是当pre数组不等于0的时候,使答案数组等于pre数组,pre数组=pre[pre]......然后答案数组编号++就可以了

优化:

假设a[i]>a[j],且f[i]<=f[j],那么a[i]就已经没有用了,比如说1,4,3,5,易得它们的最长上升子序列长度分别为1,2,2,3,其中4和3就是上升子序列长度一样,而且前面的数小于后面的数,这样前面的数就没有用了,可以删掉。这是什么原理呢,其实整体感知一下就可以:

如果一个子序列的末尾数越小,那么可以接在它后面的上升的数就越多,也就是越有可能,如果能接在小的数后面,那也肯定能接在大的数后面,并且接在小数后面更优,所以就可以更新掉,核心代码如下:

int z[233],f[233],a[233];//a数组和f数组还是原本的含义,新开的z数组的意思是编号,是位置,z[i]=j的含义就是以a[j]为结尾的最长不下降子序列长度为i

int cnt=0//定义一个变量,含义就是当前最长不下降子序列的长度

for(int i=1;i<=n;i++)

{

f[i]=1;//初始化

(以下两行循环可以用二分优化,但是我不会......)

for(int j=1;j<=cnt;j++)

if(a[z[j]]<=a[i]) f[i]=max(f[i],j+1);//如果存储在以这个数为末尾不下降子序列长度为j的数小于等于当前的数,那么这个数为末尾的不下降子序列就应该是本身和j+1的最大值

if(f[i]>cnt) cnt++,z[cnt]=i;//如果当前的不下降子序列长度已经超过了最大值,那么更新最大值,同时将末尾数存入数组

else//如果没有超过

{

if(a[i]

}

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值