LintCode 1858: Set of boxes (俄罗斯套娃,LIS二维经典题)

本文探讨了一种算法问题,即如何在运输过程中最大化箱子的堆放层数,通过将箱子按长度和宽度排序并使用动态规划或单调递增队列方法找到最多能堆放多少层箱子。

1858. Set of boxes

We have a lot of boxes to transport. To save space, we put the boxes together. Give you the length and width of each box. What is the maximum number of layers?

Example

Input: boxes = [[5,4],[6,4],[6,7],[2,3]]
Output: 3
Explanation:The maximum number of boxes is 3 ([2,3] => [5,4] => [6,7]).
Input: boxes = [[1,5],[6,2]]
Output: 2
Explanation:The maximum number of boxes is 2 ([1,5] => [2,6]).

Notice

  • The input may not be length in front, wide in back.You can see Examle 2.
  • You can rotate the box, but make sure that the four sides are parallel to the four sides of the original box, that is, do not place it diagonally.
  • This number of boxes is between [1,50000]
  • The thickness of the box is not negligible, so it must be strictly smaller

Input test data (one parameter per line)How to understand a testcase?

这题的思路有个难点是要把箱子都按长对长,宽对宽排列,因为这样的话肯定比不管长宽乱比结果来得好。所以每个箱子要按长宽排好序。

解法1:DP,时间复杂度O(n^2),会超时。

注意operator<的第二维比较可以是y>a.y,也可以是y<a.y。因为dp里面我们会同时比较x和y。

struct Node {
    int x;
    int y;
    Node(int _x = 0, int _y = 0) : x(_x), y(_y) {}
    bool operator < (const Node & a) {
        if (x < a.x) return true;
        else if (x == a.x) return y < a.y;
        return false;
    }
};


class Solution {
public:
    /**
     * @param boxes: 
     * @return: the number of boxes
     */
    int maxBoxes(vector<vector<int>> &boxes) {
        int n = boxes.size();
        if (n == 0) return 0;

        vector<int> dp(n, 1);
        vector<Node> nodes(n);
        for (int i = 0; i < n; ++i) {
            if (boxes[i][1] > boxes[i][0]) {
                swap(boxes[i][0], boxes[i][1]);
            }
            nodes[i] = Node(boxes[i][0], boxes[i][1]);
        }
        sort(nodes.begin(), nodes.end()); 
        
        int maxLayers = 1;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < i; ++j) {
                if (nodes[i].x > nodes[j].x && nodes[i].y > nodes[j].y) {
                    dp[i] = max(dp[i], dp[j] + 1);
                }
            }
            maxLayers = max(maxLayers, dp[i]);
        }
        return maxLayers;
    }
};

解法2:单调递增队列。

这道题类似LIS(LintCode 76)那道题,不过这里是2维的,更难一点。

难点:排序的时候,第1维升序,第2维降序。假设排序后队列如下:

(1,8)
(2,3)
(5,4)
(5,2)
(6,7)
(6,4)

为什么第2维要降序呢?因为看下面的例子,(5,4)排在(5,2)前面。单调递增队列轮到(5,2)的时候,binary search返回的序号就是(5,4)的序号(lower_bound,按y来搜索),所以它会踢掉(5,4)。如果第2维升序的话,那么(5,2)排在(5,4)前面,单调递增队列先把(5,2)放进来,再看(5,4)的时候因为4>2,所以(5,4)也会放进队列,这样队列里面有2个箱子第1维都是5,就不对了。
 

struct Node {

    int x;

    int y;

    Node(int _x = 0, int _y = 0) : x(_x), y(_y) {}

    bool operator < (const Node & a) {

        if (x == a.x) return y > a.y;   //note: y < a.y is wrong

        else return x < a.x;

    }

};



//return the first node that has y >= target, just like lower_bound

//int binarySearch(vector<Node> & nodes, int target) {

int binarySearch(vector<int> &nums, int target) {

    if (nums.size() == 0) return -1;

    int start = 0, end = nums.size() - 1;

    while(start + 1 < end) {

        int mid = start + (end - start) / 2;

        if (nums[mid] >= target) {

            end = mid;

        } else {

            start = mid;

        }

    }

    if (nums[start] >= target) return start;

    return end;

}



class Solution {

public:


    int maxBoxes(vector<vector<int>> &boxes) {

        int n = boxes.size();

        if (n == 0) return 0;

        vector<Node> nodes(n);

        

        for (int i = 0; i < n; ++i) {

            if (boxes[i][0] > boxes[i][1]) {

                swap(boxes[i][0], boxes[i][1]);

            }

            nodes[i] = Node(boxes[i][0], boxes[i][1]);

        }

        sort(nodes.begin(), nodes.end()); 

        vector<int> monoIncSeqs(n, INT_MAX);

        for (int i = 0; i < n; ++i) {

            int k = binarySearch(monoIncSeqs, nodes[i].y);

            //int k = lower_bound(monoIncSeqs.begin(), monoIncSeqs.end(), nodes[i].y) - monoIncSeqs.begin();

            monoIncSeqs[k] = nodes[i].y;

        }

        for (int i = n - 1; i >= 0; --i) {

            if (monoIncSeqs[i] != INT_MAX) return i + 1;

        }

        return 0;

    }

};

注意,类似题目可以直接用pair做,就不需要再定义节点了。另外,可以直接调用lower_bound()函数,也就不用再写binary_search()了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值