[Leetcode] 265. Paint House II

这一题其实不是很难。首先,很明显这是一道dp题。dp的方程式也不难推出
如果说f(i, j)是在第i个房间涂第j种颜色的最优解,那么方程式就是

f(i, j) = costs(i, j) + min(f(i - 1, x)) 其中x 是(0, 1, 2, 3, ... k)中除去 j的集合(因为不能相邻房间不能同色)。

那么很显而易见,走完n个房子k种颜色,需要的复杂度是O(nk^2),主要是 min(f(i - 1, x))的操作是O(k)的。所以这个Follow up的关键就是把这个O(k)的操作变成O(1)的。首先我们需要一个O(k)的空间去装当前这一行正在计算的f(i, j),我们称之为curLine

其中一个次优解是用到一个多余的O(k)的空间的,我们叫它为prevMin。就是用一个大小为k的数组去记录每一个位置上的,min(f(i - 1, x))。可以看得出prevMin是通过curLine得到的,也就是计算第i + 1个house的k种颜色的情况的时候。prevMin是通过第i个house计算得出的curLine得到的。 做法可以是这样:当我们计算出curLine在第i个house的解集之后,正反各扫一遍curLine,扫的时候维持一个当前最小值。譬如说,当我们扫到第j个位置的时候,正扫的时候我们可以有min(curLine[0], curLine[1] .. curLine[j - 1]),反扫的时候我们可以有min(curLine[j + 1], curLine[j + 2]... curLine[k - 1])。这两个值中的更小者便是prevMin[j]。 所以这个时候,我们就用O(k)的方式,得到了所有的min(f(i - 1, x))。根据这个描述,可以得到代码如下。

    public int minCostII(int[][] costs) {
        if (costs.length == 0) return 0;
        int[] curLine = new int[costs[0].length];
        int[] minPrev = new int[costs[0].length];
        int min = Integer.MAX_VALUE;
        for (int i = 0; i < costs.length; i++) {
            min = Integer.MAX_VALUE;
            for (int j = 0; j < costs[0].length; j++) {
                curLine[j] = costs[i][j] + minPrev[j];
                if (j != 0) {
                    min = Math.min(min, curLine[j - 1]);
                }

                minPrev[j] = min; 
            }
            
            min = curLine[curLine.length - 1];
            for (int j = curLine.length - 2; j >= 0; j--) {
                minPrev[j] = Math.min(min, minPrev[j]);
                min = Math.min(min, curLine[j]);
            }                         
        }
        return min;
    }

跟描述上略有差异的是我们的正扫是在构建当前的curLine的过程中完成的,所以我们只需要多一步反扫就可以了。
稍微能在空间上有所突破的是我们可以少用一个prevMin数组。事实上,我们不完全需要之前一个房子的所有颜色对应的最小值,我们只需要之前一个房子对应的所有颜色中的最小花费和第二小的花费以及最小花费所对应的颜色就可以了。就是在计算当前房子的花费的时候,我们只需要看一下目前在计算的颜色和计算到上一个房子的的最小花费所对应的颜色是否一样,是的话就用当前房子当前颜色cost[i][j] + secondPrevMinCost,否则就是cost[i][j] + prevMinCost就可以了。所以我们就把维护一个数组,变成维护两个值和它所对应的颜色。如果说上一种解法用O(1)解决min(f(i - 1, x))的关键是“遍历一个数组a,然后得到另一个数组b,其中对于任意i, b[i]是数组a除去a[i]以外的最小值”,那么这一种解法就是“遍历一个数组a,得到数组里最小和第二小的值,并且知道它们的index”,这个就容易许多,就不具体阐述了,直接给出代码吧。

    public int minCostII(int[][] costs) {
        if (costs.length == 0) return 0;
        int[] curr = new int[costs[0].length];
        int[] min =  {0, Integer.MAX_VALUE};
        int[] secondMin = {0, Integer.MAX_VALUE};
        int[] nextMin = {0, Integer.MAX_VALUE};
        int[] nextSecondMin = {0, Integer.MAX_VALUE};
        for (int i = 0; i < costs.length; i++) {
            nextMin[1] = nextSecondMin[1] = Integer.MAX_VALUE;            
            for (int j = 0; j < costs[0].length; j++) {
                curr[j] = costs[i][j];
                if (i != 0) curr[j] += j == min[0] ? secondMin[1] : min[1];
                
                if (curr[j] < nextMin[1]) {
                    System.arraycopy(nextMin, 0, nextSecondMin, 0, 2);
                    nextMin[0] = j;
                    nextMin[1] = curr[j];                    
                } else if (curr[j] < nextSecondMin[1]) {
                    nextSecondMin[1] = curr[j];
                    nextSecondMin[0] = j;
                }
            }
            System.arraycopy(nextMin, 0, min, 0, 2);
            System.arraycopy(nextSecondMin, 0, secondMin, 0, 2);
        }
        return min[1];
    }

猜你喜欢

转载自blog.csdn.net/chaochen1407/article/details/81509681