您的位置:首页 > 其它

如何用O(nlogn)的的速度求解最长上升(下降)子序列

2010-07-12 11:16 337 查看
打导弹解题有感

求一个序列的最长上升子序列,可以用贪心的思想来做。

例:389 207 155 300 299 170 158 65

要求它的最长递减子序列,具体步骤如下:

389

389 207

389 207 155

389 300 155 //207被300替换了,207是三个数中刚好小于300的数,207能接的后序列,300照样能接,而且有更大的空间可以接

389 300 299 //此次用299替换155,一样是因为有更大的空间可用,在这里就体现了用300来替换207的正确性

389 300 299 170

389 300 299 170 158

389 300 299 170 158 65

这样就得到了一个最长递减子序列,而且更有可能接更多的数

由于在这个贪心的过程中,辅助数组是有序的,就可以用二分查找来优化,最终将时间优化到O(nlogn)

对于打导弹这道题,求最少的导弹拦截系统的个数,实践最终证明,他等价于求最长上升子序列,类似于对偶优化问题,看它的贪心过程就可知道,它是如何等价于求最长上升子序列的,过程如下:

389

207

155

155 300

155 299

155 170

155 158

56 158 // 找到刚大于56的,然后替换掉,是最优的选择

代码如下:

代码

#include<stdio.h>
#define NN 10004

int tmp[NN], high[NN];
int index;
int find1(int t){
int low = 0;
int hig = index - 1;
int ans = -1;
do
{
int mid = (low + hig) / 2;
if (tmp[mid] < high[t]){
ans = mid;
hig = mid - 1;
}else{
low = mid + 1;
}
}while (low <= hig);
return ans;
}
int find2(int t){
int low = 0;
int hig = index - 1;
int ans = -1;
do
{
int mid = (low + hig) / 2;
if (tmp[mid] >= high[t]){
ans = mid;
hig = mid - 1;
}else{
low = mid + 1;
}
}while (low <= hig);
return ans;
}
int main()
{
int i, n, t;
while (scanf("%d", &n)!= EOF){
if (n == 0) break;
for (i = 0; i < n; i++){
scanf("%d", &high[i]);
}

tmp[0] = high[0];
index = 1;
for (i = 1; i < n; i++){
t = find1(i);
if (t == -1){
tmp[index++] = high[i];
}else
tmp[t] = high[i];
}
printf("%d\n", index);

tmp[0] = high[0];
index = 1;
for (i = 1; i < n; i++){
t = find2(i);
if (t == -1){
tmp[index++] = high[i];
}else
tmp[t] = high[i];
}
printf("%d\n", index);
}
return 0;
}


补充:这种方法只能求得长度,如果要输出这个序列,还需要另外的算法。sgu199还是不能做!!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: