USACO 3.1 Shaping Regions

Shaping Regions

N opaque rectangles (1 <= N <= 1000) of various colors are placed on a white sheet of paper whose size is A wide by B long. The rectangles are put with their sides parallel to the sheet's borders. All rectangles fall within the borders of the sheet so that different figures of different colors will be seen.

The coordinate system has its origin (0,0) at the sheet's lower left corner with axes parallel to the sheet's borders.

PROGRAM NAME: rect1

INPUT FORMAT

Line 1:A, B, and N, space separated (1 <= A,B <= 10,000)
Lines 2-N+1:Five integers: llx, lly, urx, ury, color: the lower left coordinates and upper right coordinates of the rectangle whose color is `color' (1 <= color <= 2500) to be placed on the white sheet. The color 1 is the same color of white as the sheet upon which the rectangles are placed.

SAMPLE INPUT (file rect1.in)
20 20 3
2 2 18 18 2
0 8 19 19 3
8 0 10 19 4

INPUT EXPLANATION
Note that the rectangle delineated by 0,0 and 2,2 is two units wide and two high. Here's a schematic diagram of the input:
11111111111111111111
33333333443333333331
33333333443333333331
33333333443333333331
33333333443333333331
33333333443333333331
33333333443333333331
33333333443333333331
33333333443333333331
33333333443333333331
33333333443333333331
33333333443333333331
11222222442222222211
11222222442222222211
11222222442222222211
11222222442222222211
11222222442222222211
11222222442222222211
11111111441111111111
11111111441111111111
The '4's at 8,0 to 10,19 are only two wide, not three (i.e., the grid contains a 4 at 8,0 and a 4 at 8,1 but NOT a 4 at 8,2 since this diagram can't capture what would be shown on graph paper).

OUTPUT FORMAT
The output file should contain a list of all the colors that can be seen along with the total area of each color that can be seen (even if the regions of color are disjoint), ordered by increasing color. Do not display colors with no area.

SAMPLE OUTPUT (file rect1.out)
1 91
2 84
3 187
4 38

    看到这题,最直观的想法就是模拟,复杂度为O(ABn) 。但是数据的范围太大(矩阵长宽都是10000,修改次数为1000),肯定超时,只能另辟蹊径。
    由于修改的次数较少,而且每次修改都是修改一个矩形,因此考虑只存储矩形的上下左右四个边界坐标和矩形内的颜色,这样就不用一个点一个点的存储了。
    那么现在的问题就是,新进来一个矩形,与之前哪些矩形重合?如果重合要如何修改?
重合的判断:
    如果一个矩形的下坐标比另一个矩形的上坐标还要靠上,那么肯定不相交。类似的,一个矩形的上坐标比另一个矩形的下坐标还要靠下,也不相交;左右同理。
    如果以上都不满足,那么必定是有重合的区域(反证法易证)。
重合之后的修改:
    如果重合,考虑将原来的矩形分成若干个新的小矩形,使得重合部分消失。但是重合的情况实在太多,一个一个讨论的话代码非常冗长,同时容易造成遗漏。那么如何以比较统一的方式进行处理?
    我们先从矩形的下边进行考虑,假设新矩形(红色)的下边位于原来矩形(绿色)下边的上方,情况大致分为以下几种:

可以发现,以上这些情况,原始矩形最下边都可以划分出一个小矩形来,如下图黄色区域:

    这个区域的上下左右四个坐标十分容易求出。假如新矩形的下边位于原来矩形下边的下方,那么就不存在这样的区域。
    接下来我们考虑左边。同上一步一样,如果新矩形的左边位于原来矩形左边的右方,那么原始矩形的左边也能划分出一个小矩形。但是这里有个问题,上一步中是否划出下面一小块新矩形,对这一步中左边部分矩形的划分会有影响。如图b1、b2,b1的原始矩形下面已经得到划分(黄色区域),因此左边待划分的区域如b3中蓝色部分;而b2的原始矩形下面没有划分,因此整个左边都是待划分区域,如图b4。
其实这个问题比较好解决,只要比较两个矩形的下边,取上方的值即可。
        右边的情况和左边类似,不再赘述。
        最后考虑上边。以图5~8为例,黄色、蓝色区域为前几步已划分区域,绿色为待划分的区域。可以发现,绿色区域的左右边界都是不一样的。
        其实这里也比较容易处理:取两个矩形左边界靠右的值和两个矩形右边界靠左的值作为新边界即可。
        以上两个问题得到解决,整个算法的思路就很清晰了:
        开一个数组存放矩形,每次新来一个矩形,先对已有矩形进行扫描,如果没有重合区域,则不处理;否则按照上述方法对原始矩形划分,将划分得到的新矩形存进数组末尾,同时删掉原始矩形。最后再将新来的矩形放入数组。循环n次即可。
        以上算法中,存在对数组元素的删除操作,导致数组前移,比较耗时,我的处理方法类似一个队列:不管是否存在重合区域,都删除原始矩形。若重合,将划分的小矩形添加到数组末尾;否则将原始矩形添加到数组末尾。这里的删除仅仅是指将数组的开始下标+1,不涉及真正的删除操作。最后结果如下:

Compiling...
Compile: OK

Executing...
   Test  1: TEST OK [0.011 secs, 11192 KB]
   Test  2: TEST OK [0.000 secs, 11192 KB]
   Test  3: TEST OK [0.011 secs, 11192 KB]
   Test  4: TEST OK [0.000 secs, 11192 KB]
   Test  5: TEST OK [0.011 secs, 11192 KB]
   Test  6: TEST OK [0.011 secs, 11192 KB]
   Test  7: TEST OK [0.011 secs, 11192 KB]
   Test  8: TEST OK [0.011 secs, 11192 KB]
   Test  9: TEST OK [0.011 secs, 11192 KB]
   Test 10: TEST OK [0.011 secs, 11192 KB]
   Test 11: TEST OK [0.043 secs, 11192 KB]
All tests OK.

由于矩形的数量不好估计,一开始采用了vector进行存储,结果在跑最后一个点竟然出现bad_alloc异常,这是得有多大的数据啊……只好把数据copy下来本机跑,顺利通过,发现最后一个点数组容量达到30w+。把vector改成定长数组,顺利通过。
PS:实际上即使不用类似队列的方式,直接在vector中删除矩形,时间也是很短的,而且内存占用大幅减少。


#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

struct rect
{
    int l, d, r, u, c;

    void cfg(int rl, int rd, int rr, int ru, int rc)
    {
        l = rl, d = rd, r = rr, u = ru, c = rc;
    }
};

int size = 0;
rect rect_list[393216];

int main()
{
    freopen("rect1.in", "r", stdin);
    freopen("rect1.out", "w", stdout);

    rect rt, *p;

    int start, end, v;

    int A, B, n;
    int l, d, r, u, c;

    int color[2501] = { };

    cin >> A >> B >> n;
    rect_list[size++].cfg(0, 0, A, B, 1);

    for (start = 0; n--; )
    {
        cin >> l >> d >> r >> u >> c;
        end = size;

        rect_list[size++].cfg(l, d, r, u, c);

        for (v = start; v != end; v++)
        {
            p = &rect_list[v];

            if (l >= p->r || d >= p->u || r <= p->l || u <= p->d)
            {
                rect_list[size++] = rect_list[v];
                continue;
            }

            if (d > p->d)
                rect_list[size++].cfg(p->l, p->d, p->r, d, p->c);
            rt.d = max(d, p->d);

            if (l > p->l)
                rect_list[size++].cfg(p->l, rt.d, l, p->u, p->c);
            rt.l = max(l, p->l);

            if (r < p->r)
                rect_list[size++].cfg(r, rt.d, p->r, p->u, p->c);
            rt.r = min(r, p->r);

            if (u < p->u)
                rect_list[size++].cfg(rt.l, u, rt.r, p->u, p->c);
        }
        start = end;
    }

    end = size;
    for (v = start; v != end; v++)
    {
        p = &rect_list[v];
        color[p->c] += (p->u - p->d) * (p->r - p->l);
    }

    for (v = 1; v <= 2500; v++)
        if (color[v])
            cout << v << " " << color[v] << endl;

    return 0;
}




                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值