11.2 Leetcode 2116 摧毁小行星

136 阅读1分钟

2126. 摧毁小行星 - 力扣(LeetCode)

image.png

解题思路

易想到的方法就是都数据进行排序,然后先撞击小的,一次一次累计。

排序的时间复杂度为 O(nlogn)

还有一种线性时间复杂度的方法

将数据分为 [0,1],[2,3],[4,7],...,[2k,2k+11][0,1],[2,3],[4,7],...,[2^{k},2^{k+1} - 1] , 即在将数字用二进制表示,分别需要 1 位、2 位、3 位、…… 、k + 1 位。

便可发现,在每个区间内,只要小于区间内最小的,他就能摧毁掉这个区间内的所有。

思路:

  1. 将所有数字分别装入各自的区间,题目已经给出其数值最大值为 10510^5,即最多需要 17 个区间(216<105<2172^{16} < 10^5 < 2^{17}
  2. 和每个区间的最小值进行比较,如果小于,就能摧毁掉,否则就不能。
  3. 能摧毁掉就加上这个区间的和,进入下一区间。

有读者可能会想,可以通过八进制或其他更改进制把数值分进不同的区间,这样占用的空间就会更小。

分成八进制的话,那 当小于最小值就能摧毁整个区间 就不能成立

把数值分成 [0,7],[8,63],...,[8k,8k+11][0, 7],[8,63],...,[8^k, 8^{k+1} - 1],当数值大于等于这个区间的最小值,他加上这个最小值,它就不能保证大于等于整个区间的所有数值。

明显使用二进制是可以做到的。

代码

int getSh(int n) {
    int count = 0;
    while(n != 1) {
        n >>= 1;
        count++;
    }
    return count;
}

bool asteroidsDestroyed(int mass, int* asteroids, int asteroidsSize){
    long sum[17] = {0};
    int min[17] = {0};

    for(int i = 0; i < asteroidsSize; i++) {
        int t = getSh(asteroids[i]);
        if(asteroids[i] < min[t] || min[t] == 0) min[t] = asteroids[i];
        sum[t] += asteroids[i];
    }

    long long newmas = mass;
    for(int i = 0; i < 17; i++) {
        if(newmas >= INT_MAX) return true;
        if(newmas < min[i]) return false;
        newmas += sum[i];
    }

    return true;
}

结果

image.png