最长递增子序列

前段时间闲着没事做就上网看了看一些编程比赛题,我不是大牛,当然从简单的看起,做的时候腾讯的一道“豆豆”的题让我很感兴趣,题目是这样的:

有只企鹅叫豆豆,总是被别的企鹅欺负。豆豆在长期的隐忍之后,掌握了所有企鹅的高度和攻击力强度,还得到了一把黄金剑。在拥有了黄金剑以后,豆豆终于可以展开绝地大反击。但这把黄金剑的用法却很奇怪。
首先,豆豆第一次可以选择任何一只企鹅开始挑战。豆豆这一次必胜。
再次,当豆豆已经挑战过某一只企鹅后,再下一次的挑战对象只能是比上一名对手高,且比上一名对手攻击力强的企鹅。这样豆豆必胜。否则黄金剑会觉得打的没意思而故意发脾气输掉。豆豆还会被大家集体暴打。
面对着这把脾气很大的黄金剑,豆豆想请你帮助他计算一下,他最多可以连续击败多少只企鹅?

Input
第一行:一个数据n,代表企鹅群里除了豆豆一共有n(1 ≤ n ≤ 1000)只企鹅。
第2至第n+1行:每行2个数字。第i+1行的第一个数字为企鹅i的高度。第i+1行的第二个数字为企鹅i的攻击力。0 ≤ 高度,攻击力 ≤ 1,000,000。

Output
一个数。代表豆豆最多可以连续击败的企鹅数。 
Sample Input

Sample Input #1 
3
1 3
3 2
2 4
Sample Output #1
2

Sample Input #2 
5
10 1
9 2
7 3
6 4
5 5
Sample Output #2 
1
初看这题就想到了DP,以前大牛们不是说过:一切皆为DP吗!呵呵,我思索了下,题目铺垫很长,但输入、输出却很明确,两个限制性条件:身高,攻击力。
我最先先想到的是逐个遍历每只企鹅,然后依次比较能攻击的数目,取最大的那个。事实上我仔细一想,这样是非常麻烦,两个条件的要求使得比较没有看起来
那么容易,这时我想如果能减少一个条件这个问题就非常好解决了,比如不考虑身高,这么一想就明确了,先把身高排序,这样就只用比较攻击力了,而且由于
身高排好序了,攻击力的比较只需要和身高比自己高的企鹅进行比较,这么讲太抽象,我将排好序的企鹅列表如下:

企鹅总数为:5
5 5

6 4

7 3

9 2

10 1

下面只需要比较企鹅的攻击力,如从5 5这只企鹅开始,依次用5和4,3,2,1,比较找到比5大的个数,记录进最后的个数max,然后用6 4这只企鹅,依次用4和3,2,1,比较找到比4大的个数,和max比较,如果比max大就赋值给max。遍历一遍后就能找出最大能攻击的企鹅个数,于是我再纸上写下了以下的代码:

但我的组长看到我写的代码后马上指出了我的错误所在,这种方法是相当于假定一个节点为子序列头结点后,遍历后面的节点,之后比它大,就纳入子序列中,但是这样做是有问题的;想想这样一个序列:1 9 2 3 4 5;当选择了1为头结点之后,要是你选择9为头结点,后面的节点就都不能选了。而实际上不选择9,你可以得到长度为5的最长递增序列!而你的程序是一下子把9 2 3 4 5都拿到了子序列中,应该是有问题的,还需要考虑一下。

于是我又仔细想了想上面的程序确实没有考虑全面,正如组长所说的那样,对于类似192345这样的例子就会出错,那么这个最长递增子序列怎么求呢,我微微想了下,可以把我想要求的最小子序列存起来,依次将递增的数值存入数组,先将第一个数字存入数组,然后遍历源序列,遇到比子序列中最后一个数字大的数就加入子序列,如果比最后一个小,就再从头遍历子序列找到刚好比它大的数,将它替换这个数之前的数,这样只需遍历一遍原序列就能找出最长递增子数列的长度,在排好序后,修改所写代码如下:

这样实现成功之后,我又仔细看了下这段程序,觉得他还不够优美,细细算了下时间复杂度,达到了O(N2),品味了一下,觉得在遍历子序列时还可以优化,比如用二分查找法,于是我进一步修改了代码,使用二分查找来实现替换子序列中的数,代码修改部分如下:


这样一来在遍历源序列的时间复杂度就降到了O(NlogN),可能还有更佳的方法,敬请拍砖!!

最后也许你会说我为什么没有给出一个求出最长递增子序列的算法,对!我的程序并不能求出这样的子序列,仅仅只能算出它的长度,不过只需要稍加修改我的程序就可以写出这样一个算法,有兴趣的朋友可以在下面回帖附上自己的代码,非常希望能和各位大牛交流,呵呵!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值