[itint5]摆放窗口

http://www.itint5.com/oj/#47

一种做法是:把矩形所占的方格都设为-1,就是个最大子矩阵和问题。复杂度o(w^2*h)或o(w*h^2),空间W*H
猜想应用场景是:电脑屏幕上已经有了n个聊天框,新建一个聊天框,放在屏幕的哪个位置最好。客户端计算的话,空间复杂度太高的算法应该是没法实际应用的。这种方法OJ也会空间超出。

另一种做法(贪心思想,和一个矩形覆盖最小):
所求矩形的上边要么贴着边界,要么贴着某个已有矩形的下边
所求矩形的下边要么贴着边界,要么贴着某个已有矩形的上边
所求矩形的左边要么贴着边界,要么贴着某个已有矩形的右边
所求矩形的右边要么贴着边界,要么贴着某个已有矩形的左边
总可以找到一个满足最小覆盖条件的矩形,这个矩形的边要么与大窗口的边缘重合,要么和已知矩形的边重合。可以使用常见的贪心思想证明,假设有任意一个满足最小覆盖条件的矩形,总可以将其移动到与已有的边重合。

这样,只需要枚举O(n^2)的左上点坐标,总的时间复杂度O(n^3)。

可以这样理解,假设已经把矩形放在一个位置了,先只考虑上下移动(左右同理),那么在上下边遇到另一条横边之前,往上移动覆盖面积要么单调递增要么单调递减(也可以覆盖不变),往下移动单调性相反。那么总能移动到和某一条边重合并比原来覆盖面积相等或更小的。所以假设已经找到一个满足最小覆盖条件的矩形,那么可以将其移动到与已有的边重合。
注意:求两个矩形相交部分的面积的写法也很值得参考。

int calOverlapping(Rect &a, Rect &b) {
    int x = max(0, min(a.x2, b.x2) - max(a.x1, b.x1));
    int y = max(0, min(a.y2, b.y2) - max(a.y1, b.y1));
    return x * y;
}


int minOverlapping(vector<Rect> &rects, int W, int H, int w, int h) {
    if (rects.size() == 0) return 0;
    vector<int> x, y;
    x.push_back(0); x.push_back(H-h);
    y.push_back(0); y.push_back(W-w);
    
    for (int i = 0; i < rects.size(); i++) {
        if (rects[i].x1 - h >= 0) x.push_back(rects[i].x1 - h);
        if (rects[i].y1 - w >= 0) y.push_back(rects[i].y1 - w);
        if (rects[i].x2 + h <= H) x.push_back(rects[i].x2);
        if (rects[i].y2 + w <= W) y.push_back(rects[i].y2);
    }
    
    int ans = w * h; // max is fully overlapped
    for (int i = 0; i < x.size(); i++) {
        for (int j = 0; j < y.size(); j++) {
            Rect r;
            r.x1 = x[i]; r.x2 = x[i] + h;
            r.y1 = y[j]; r.y2 = y[j] + w;
            int cal = 0;
            for (int k = 0; k < rects.size(); k++) {
                cal += calOverlapping(rects[k], r);
            }
            if (cal < ans) ans = cal;
        }
    }
    return ans;
}

  

posted @ 2014-01-22 15:08  阿牧遥  阅读(284)  评论(0编辑  收藏  举报