算法 : 最长升序降序序列
2010-03-31 20:45
113 查看
引用 :算法:最长升序降序序列,7 5 6 8 10 7 9 3 8 7 4 1 8 9 4,则最长的升序序列为5, 6, 8,
10,最长的降序序列为8,7,4,1
看了一些网上的资料,最长的升序/降序 好像不是非要在彼此挨着的吧?
最长的升序序列 不可以为 :5 6 7 8 9 吗?
最长的降序序列 不可以为 :10 9 8 7 4 1 吗?
引用:Lifeng Wang
主动去创造环境,否则你无法设计人生
个人C++ 没学过,C#倒很熟悉,C嘛,大一学到指针就结课了,后面一些除了校门用得更多的东西反倒没学,悲哀啊。
再加上本人有点笨,还是看不懂 什么 时间和空间复杂度的 问题啊。。。。想学算法,还真 不容易啊。。。还望高手指点。
一、算法思想
算法还是容易想到的,两
重循环
DP
即可。不过
如果数据规模最大可以达到几十万甚至更大
,经典的
O(n^2)
的动态规划算法明显会超时。我们需要寻找更好的方法来解决是最长上升子序列问题。以
下以最长递增子序列为例进行说明:
先回顾经典的
O(n^2)
的动态规划算法,设
A[i]
表示序列中的第
i
个数,
F[i]
表示从
1
到
i
这一段中以
i
结尾的最长上升子序列的长度,初始时设
F[i] = 0(i = 1, 2, ..., len(A))
。
则有动态规划方程:
F[i] = max{1, F[j] +
1} (j = 1, 2, ..., i - 1,
且
A[j] < A[i])
。
现在,我们仔细考虑计算
F[i]
时的情况。假设有两个元素
A[x]
和
A[y]
,满足
(1)x < y < i (2)A[x] < A[y] < A[i]
(3)F[x] = F[y]
此时,选择
F[x]
和选择
F[y]
都可以得到同样的
F[i]
值,那么,在最长上升子序列的这个位置中,应该选择
A[x]
还是应该选择
A[y]
呢?
很明显,选择
A[x]
比选择
A[y]
要好。因为由于条件
(2)
,在
A[x+1] ... A[i-1]
这一段中,如果存在
A[z]
,
A[x] < A[z] < a[y]
,则与选择
A[y]
相比,将会得到更长的上升子序列。
再根据条件
(3)
,我们会得到一个启示:根据
F[]
的值进行分类。对于
F[]
的每一个取值
k
,我们只需要保留满足
F[i] = k
的所有
A[i]
中的最小值。设
D[k]
记录这个值,即
D[k] = min{ A[i] } ( F[i] = k )
。
注意到
D[]
的两个特点:
(1)
D[k]
的值是在整个计算过程中是单调不上升的。
//
此处需要特别注意
!!!
关键之所在
!
(2)
D[]
的值是有序的,即
D[1] < D[2] < D[3] < ... < D
。
利用
D[]
,我们可以得到另外一种计算最长上升子序列长度的方
法。设当前已经求出的最长上升子序列长度为
len
。
先判断
A[i]
与
D[len],
若
A[i] > D[len]
,则将
A[i]
接在
D[len]
后将得到一个更长的上升子序列
,len = len + 1,D[len+1] = A[i];
否
则
,
在
D[1]..D[len]
中
,
找到最大的
j,
满足
D[j] < A[i].
令
k = j + 1,
则有
D[j] < A[i] <= D[k]
,将
A[i]
接在
D[j]
后将得到一个更长的上升子序列
,
同时更新
D[k] = A[i].
最后,
len
即为所要求的最长上升子序列的长度。
在上述算法中
,
若使用朴素的顺序查找在
D[1]..D[len]
查找
,
由于共有
O(n)
个元素需要计算
,
每次计算时的复杂度是
O(n),
则整个算法的时间复杂度为
O(n^2),
与原来的算法相比没有任何进步
.
但是由于
D[]
的特点
(2),
我们在
D[]
中查找时
,
可以使用二分查找高效地完成
,
则整个算法的时间复杂度下降为
O(nlogn),
有了非常显著的提高
.
需要注意的是
,D[]
在算法结束后记录的并不是一个符合题意的最长上升子序
列
.
这
个算法还可以扩展到整个最长子序列系列问题
,
整
个算法的难点在于二分查找的设计
,
需
要非常小心注意
.
二、
实现
// By Fandywang
2008.7.21
// Call: LIS(a,
n);
求最大递增/
上
升子序列(
如果为最大非降子序列,
只需把上面的注释部分给与替换)
const
int
N =
1001;
int
a
, f
, d
; // d[i]
用于记录a[0...i]
的最大长度
int
bsearch(const
int
*f, int
size, const
int
&a)
{
int
l=0, r=size-1;
while
( l <= r )
{
int
mid = (l+r)/2;
if
( a > f[mid-1] && a <=
f[mid] ) return
mid; // >&&<=
换为: >= && <
else
if
( a
< f[mid] ) r = mid-1;
else
l = mid+1;
}
}
int
LIS(const
int
*a, const
int
&n){
int
i, j, size = 1;
f[0]
= a[0]; d[0] = 1;
for
( i=1; i < n; ++i ){
if
( a[i] <=
f[0] ) j = 0;
// <=
换为: <
else
if
(
a[i] > f[size-1] ) j = size++;
// >
换为: >=
else
j = bsearch(f, size, a[i]);
f[j]
= a[i]; d[i] = j+1;
}
return
size;
}
三、
学以致用
JOJ 1829
Candies
最大非下降子序列
JOJ 2162
Inuyasha And the Monsters
求和最大的非
下降子序列
(
这个我
用的是
O(n^2)
的
算法做的
,
居然还那个
了第一
...)
JOJ 2529
Chorus
最
大上升子序列
+
最大下
降子序列
JOJ 1048
Wooden Sticks
先自定义排下序
(
先按
l
从
小到大
, l
相等再按
w
从小到大
),
然后求
w
的最大下降子序列
POJ 3636
Nested Dolls
与
JOJ1048
类
似
w
从
小到大
,w
相等
h
从大到小
,
然后求
h
最大非升子序列
p.s.:
如果有不准确地方欢迎指出,如果有更好的处理,麻烦教我,多多交流!~
10,最长的降序序列为8,7,4,1
看了一些网上的资料,最长的升序/降序 好像不是非要在彼此挨着的吧?
最长的升序序列 不可以为 :5 6 7 8 9 吗?
最长的降序序列 不可以为 :10 9 8 7 4 1 吗?
引用:Lifeng Wang
主动去创造环境,否则你无法设计人生
个人C++ 没学过,C#倒很熟悉,C嘛,大一学到指针就结课了,后面一些除了校门用得更多的东西反倒没学,悲哀啊。
再加上本人有点笨,还是看不懂 什么 时间和空间复杂度的 问题啊。。。。想学算法,还真 不容易啊。。。还望高手指点。
一、算法思想
算法还是容易想到的,两
重循环
DP
即可。不过
如果数据规模最大可以达到几十万甚至更大
,经典的
O(n^2)
的动态规划算法明显会超时。我们需要寻找更好的方法来解决是最长上升子序列问题。以
下以最长递增子序列为例进行说明:
先回顾经典的
O(n^2)
的动态规划算法,设
A[i]
表示序列中的第
i
个数,
F[i]
表示从
1
到
i
这一段中以
i
结尾的最长上升子序列的长度,初始时设
F[i] = 0(i = 1, 2, ..., len(A))
。
则有动态规划方程:
F[i] = max{1, F[j] +
1} (j = 1, 2, ..., i - 1,
且
A[j] < A[i])
。
现在,我们仔细考虑计算
F[i]
时的情况。假设有两个元素
A[x]
和
A[y]
,满足
(1)x < y < i (2)A[x] < A[y] < A[i]
(3)F[x] = F[y]
此时,选择
F[x]
和选择
F[y]
都可以得到同样的
F[i]
值,那么,在最长上升子序列的这个位置中,应该选择
A[x]
还是应该选择
A[y]
呢?
很明显,选择
A[x]
比选择
A[y]
要好。因为由于条件
(2)
,在
A[x+1] ... A[i-1]
这一段中,如果存在
A[z]
,
A[x] < A[z] < a[y]
,则与选择
A[y]
相比,将会得到更长的上升子序列。
再根据条件
(3)
,我们会得到一个启示:根据
F[]
的值进行分类。对于
F[]
的每一个取值
k
,我们只需要保留满足
F[i] = k
的所有
A[i]
中的最小值。设
D[k]
记录这个值,即
D[k] = min{ A[i] } ( F[i] = k )
。
注意到
D[]
的两个特点:
(1)
D[k]
的值是在整个计算过程中是单调不上升的。
//
此处需要特别注意
!!!
关键之所在
!
(2)
D[]
的值是有序的,即
D[1] < D[2] < D[3] < ... < D
。
利用
D[]
,我们可以得到另外一种计算最长上升子序列长度的方
法。设当前已经求出的最长上升子序列长度为
len
。
先判断
A[i]
与
D[len],
若
A[i] > D[len]
,则将
A[i]
接在
D[len]
后将得到一个更长的上升子序列
,len = len + 1,D[len+1] = A[i];
否
则
,
在
D[1]..D[len]
中
,
找到最大的
j,
满足
D[j] < A[i].
令
k = j + 1,
则有
D[j] < A[i] <= D[k]
,将
A[i]
接在
D[j]
后将得到一个更长的上升子序列
,
同时更新
D[k] = A[i].
最后,
len
即为所要求的最长上升子序列的长度。
在上述算法中
,
若使用朴素的顺序查找在
D[1]..D[len]
查找
,
由于共有
O(n)
个元素需要计算
,
每次计算时的复杂度是
O(n),
则整个算法的时间复杂度为
O(n^2),
与原来的算法相比没有任何进步
.
但是由于
D[]
的特点
(2),
我们在
D[]
中查找时
,
可以使用二分查找高效地完成
,
则整个算法的时间复杂度下降为
O(nlogn),
有了非常显著的提高
.
需要注意的是
,D[]
在算法结束后记录的并不是一个符合题意的最长上升子序
列
.
这
个算法还可以扩展到整个最长子序列系列问题
,
整
个算法的难点在于二分查找的设计
,
需
要非常小心注意
.
二、
实现
// By Fandywang
2008.7.21
// Call: LIS(a,
n);
求最大递增/
上
升子序列(
如果为最大非降子序列,
只需把上面的注释部分给与替换)
const
int
N =
1001;
int
a
, f
, d
; // d[i]
用于记录a[0...i]
的最大长度
int
bsearch(const
int
*f, int
size, const
int
&a)
{
int
l=0, r=size-1;
while
( l <= r )
{
int
mid = (l+r)/2;
if
( a > f[mid-1] && a <=
f[mid] ) return
mid; // >&&<=
换为: >= && <
else
if
( a
< f[mid] ) r = mid-1;
else
l = mid+1;
}
}
int
LIS(const
int
*a, const
int
&n){
int
i, j, size = 1;
f[0]
= a[0]; d[0] = 1;
for
( i=1; i < n; ++i ){
if
( a[i] <=
f[0] ) j = 0;
// <=
换为: <
else
if
(
a[i] > f[size-1] ) j = size++;
// >
换为: >=
else
j = bsearch(f, size, a[i]);
f[j]
= a[i]; d[i] = j+1;
}
return
size;
}
三、
学以致用
JOJ 1829
Candies
最大非下降子序列
JOJ 2162
Inuyasha And the Monsters
求和最大的非
下降子序列
(
这个我
用的是
O(n^2)
的
算法做的
,
居然还那个
了第一
...)
JOJ 2529
Chorus
最
大上升子序列
+
最大下
降子序列
JOJ 1048
Wooden Sticks
先自定义排下序
(
先按
l
从
小到大
, l
相等再按
w
从小到大
),
然后求
w
的最大下降子序列
POJ 3636
Nested Dolls
与
JOJ1048
类
似
w
从
小到大
,w
相等
h
从大到小
,
然后求
h
最大非升子序列
p.s.:
如果有不准确地方欢迎指出,如果有更好的处理,麻烦教我,多多交流!~
相关文章推荐
- 算法 : 最长升序降序序列
- 算法:最长升序降序序列(最长降序序列)
- 算法:最长升序降序序列(最长升序序列)
- 算法题目二:寻找最长重复子序列(2) trie树与后缀树的应用
- 最长上升子序列(LIS)的O(nlogn) & O(n^2)算法 - 动态规划
- 最长不下降子序列的O(n*logn)算法
- “最长公共字符串子序列”问题的动态规划法算法
- 最长单调子序列(O(n^2) 和 O(nlogn) 算法)
- 最长上升子序列(LIS)长度的O(nlogn)算法
- [算法] poj 3903 最长上升子序列 dp vs (二分 nlogn)
- 最长上升子序列(LIS)长度的O(nlogn)算法
- 数组字符串那些经典算法:最大子序列和,最长递增子序列,最长公共子串,最长公共子序列,字符串编辑距离,最长不重复子串,最长回文子串
- 最长上升子序列(LIS)算法分析(做…
- 最长单调递增子序列O(NlogN)算法
- poj 3903 最长上升子序列,nlog(n)的算法
- 最长上升子序列(LIS)的两种算法
- 算法提高 最长字符序列 (递归or动态规划)
- 求两字符序列的最长公共字符子序列算法的优化过程
- 算法题目二:寻找最长重复子序列(3) 后缀树
- 算法题10 最长等差序列问题