打导弹解题有感

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

例: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还是不能做!!!!

posted on 2010-07-12 11:16  ylfdrib  阅读(1786)  评论(4编辑  收藏  举报