当年toj存在的时候,我也做过这题,4年前吧
回复 20楼 StarWing83
推箱子那个就不管它了,但导弹这个有更快的方法我感兴趣,我有想过,但没想也更快的方法,能说下更快方法的思路吗?麻烦你了,谢谢!
#include <stdio.h> #include <stdlib.h> #define N 100 int n, a[N], bn, b[N], c[N], d[N]; /* 数组a:保存输入的数,所在导弹的高度 数组b:保存最长降序数列,但不是最终结果。 数组c:保存数组b降序数列在a中的下标。 数组d:记下每段降序的连接下标,最主要就是这数组,只了解了这数组里的数据,了解程序是怎么把每一段数列对应的下标保存下来,这个算法就想通了。就这点难。 变量bn:记下降序数列长度 */ int main(void) { int i, cur; while (scanf("%d", &n) == 1) /* 先输入有几棵导弹n */ { for (i = 0; i < n; ++i) /* 对n个导弹指定高度 */ scanf("%d", &a[i]); for (i = 0, bn = -1; i < n; ++i)/* 降序数列长度初始值为:-1,循环n次 */ { if (bn == -1 || a[i] <= b[bn])/* bn等于-1时,表示测到第一棵导弹,长度bn增1,保存高度到数组b。 a[i]<=b[bn],指如测到当前导弹的高度比前一棵低,bn增1,保存这棵的高度到数组b */ b[cur = ++bn] = a[i]; else /* 否则测到的当前导弹高度大于前一棵,下面的算法是把这棵的高度插入有序的数组b里,准确的说是:找到插入点,复盖 掉这点。 */ { int low = 0, high = bn, mid; while (mid = (low + high) >> 1, low + 1 < high) { if (a[i] < b[mid]) low = mid; else if (a[i] >= b[mid]) high = mid; } if (b[low] <= a[i]) b[cur = low] = a[i]; else b[cur = high] = a[i]; } c[cur] = i;/* 保存刚入数组b的值在数组a的下标 */ d[i] = cur ? c[cur - 1] : -1;/* 记下连接,这不好理解,如各位自己编写同样算法的程序就会知道难点在这 */ } cur = c[bn];/* 到这是以找到最长的降序数列了,把这数列最后的一个数,也最小的数的下标赋给cur */ for (i = bn; i >= 0; --i)/* 循环把最长的降序数列保存到数组b,了解数组d,这循环才好了解。 */ { b[i] = a[cur]; cur = d[cur]; } for (i = 0; i <= bn; ++i)/* 输出可击落最多导弹的各个高度,也是就是最长数列 */ printf("%d ", b[i]); putchar('\n'); } return 0; }
· · while (low + 1 < high) { mid = (low + high) >> 1;/* 这个取中间值循环条件判断用不上,我把它移下来了。这样让人更好理解点 */ if (a[i] <= b[mid]) /* 加个等号 */ low = mid; else if (a[i] > b[mid]) /* 原先有一个等号,现在去掉了 */ high = mid; } if (b[low] < a[i]) /* 原先这也有一个等号,现在去掉了,这样测试数据都正确了。 */ b[cur = low] = a[i]; else b[cur = high] = a[i]; · ·
while (low + 1 < high) { mid = (low + high) >> 1;/* 这个取中间值循环条件判断用不上,我把它移下来了。这样让人更好理解点 */ if (a[i] <= b[mid]) /* 加个等号 */ low = mid; else high = mid; } if (b[low] < a[i]) /* 原先这也有一个等号,现在去掉了,这样测试数据都正确了。 */ b[cur = low] = a[i]; else b[cur = high] = a[i];