【Lintcode】1575. Spring Tour(配数学证明)

题目地址:

https://www.lintcode.com/problem/spring-tour/description

n n n组小朋友去春游,数组 A A A代表每组的人数,保证每组不超过 4 4 4人。现在有若干量容积是 4 4 4人的车,同一组小朋友必须坐同一辆车,但每辆车不一定要坐满。问至少要多少辆车。

思路是贪心。我们先想一个办法尽量让每辆车载最多的人,然后来证明方案的最优性。

先数一下人数 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4的组各有多少个,记为数组 c c c。首先,人数 4 4 4的组肯定要坐满一辆车,所以答案先加上 c [ 4 ] c[4] c[4]。接着,看人数 3 3 3的组有多少个,尽量配 c [ 3 ] c[3] c[3]个人数 1 1 1的组,这样就形成了 4 4 4个人,如果 c [ 1 ] ≥ c [ 3 ] c[1]\ge c[3] c[1]c[3],那么就凑出了 c [ 3 ] c[3] c[3] 3 + 1 3+1 3+1这样的配对,还剩 c [ 1 ] − c [ 3 ] c[1]-c[3] c[1]c[3] 1 1 1人组;如果 c [ 1 ] < c [ 3 ] c[1]< c[3] c[1]<c[3],那么就凑出了 c [ 1 ] c[1] c[1] 3 + 1 3+1 3+1这样的配对,并且不剩 1 1 1人组。无论怎样,答案都要加上 c [ 3 ] c[3] c[3],因为如果剩下 3 3 3人组没有 1 1 1人组配对的话,也必须有辆新车装载,接着还要让 c [ 1 ] c[1] c[1]减去 min ⁡ { c [ 1 ] , c [ 3 ] } \min\{c[1],c[3]\} min{c[1],c[3]},表示还剩下的 1 1 1人组。接着让 2 2 2人组两两配对上车,所以让答案加上 c [ 2 ] / 2 c[2]/2 c[2]/2 c [ 2 ] c[2] c[2]变为 c [ 2 ] % 2 c[2]\% 2 c[2]%2,表示还剩的 2 2 2人组。接着如果能配则尽量去配 1 + 1 + 2 1+1+2 1+1+2的组合,剩余 c [ 1 ] − 2 c [ 2 ] c[1]-2c[2] c[1]2c[2] 1 1 1人组,这些人就 4 4 4 4 4 4个配起来即可;如果配不出来,则由于此时 c [ 2 ] ≤ 1 c[2]\le 1 c[2]1,说明 c [ 1 ] = 1 c[1]=1 c[1]=1 0 0 0,可以分类,如果 c [ 2 ] = 1 c[2]= 1 c[2]=1,那么还需要 1 1 1辆车载 2 2 2或者 1 + 2 1+2 1+2;如果 c [ 2 ] = 0 c[2]=0 c[2]=0那有 c [ 1 ] = 0 c[1]=0 c[1]=0,所以不需要额外的车。

算法正确性证明:
首先 4 4 4人组肯定要单独排车的,这个无法优化了;接着 3 3 3人组应该尽量和 1 1 1人组配对成 3 + 1 3+1 3+1,配不出的 3 3 3人组独自上车,原因是,如果最优解不这样排,那排完 3 3 3人组之后, 1 1 1人组剩余的数量会比上述做法更多,再继续求解不会得到更优解。接下来问题转化为,有一系列人数是 1 1 1 2 2 2的组,怎么安排车辆。上面的贪心解是尽量先配对 2 + 2 2+2 2+2,然后配对 1 + 1 + 2 1+1+2 1+1+2,剩余的 1 1 1人组就 4 4 4 4 4 4个排车。可以证明这样的安排是最优的,因为如果有 2 + 2 2+2 2+2的配对而不去配,那么这些 2 2 2人组一定分在了两辆车上,那么我们可以重新组合,让那两辆车的 2 2 2人组配成一对,剩余的人安排一辆车,这样得到的解车辆数相同,所以尽量配对 2 + 2 2+2 2+2的方式是最优的。剩余的讨论就显然了。

代码如下:

public class Solution {
    /**
     * @param a: The array a
     * @return: Return the minimum number of car
     */
    public int getAnswer(int[] a) {
        // Write your code here
        int res = 0;
        int[] count = new int[5];
        for (int n : a) {
            count[n]++;
        }
        
        // 先载4人组
        res += count[4];
        
        // 再配3 + 1的组
        count[1] -= Math.min(count[1], count[3]);
        res += count[3];
        
        // 再配2 + 2的组
        res += count[2] >> 1;
        count[2] %= 2;
        
        if (count[1] >= count[2] << 1) {
        	// 再配1 + 1 + 2的组
            res += count[2];
            count[1] -= count[2] << 1;
            // 剩余的1人组每4个排一辆车
            res += (count[1] + 3) / 4;
        } else if (count[2] == 1) {
        	// 如果配不出1 + 1 + 2的组,并且2人组还有1个,则再排一辆车
            res++;
        }
        
        return res;
    }
}

时间复杂度 O ( n ) O(n) O(n),空间 O ( 1 ) O(1) O(1)

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 创作都市 设计师:CSDN官方博客 返回首页