zoukankan      html  css  js  c++  java
  • HDU2461 Rectangles 容斥定理,状态压缩

    这题简单说就是求矩形的面积并,线段树?只有20个矩形,我们可以用容斥来做。但是这个有个比较麻烦的地方就是要求出任意组合情况下的面积并,试过几次每次进行求解的写法都一一超时了。这里选择在dfs的时候直接枚举题目将询问的状态,只要当前状态是其子集的话,就直接加到上面。最后M次询问就能够在O(1)的时间内完成了。296MS水过了。

    代码如下:

    #include <cstring>
    #include <cstdlib>
    #include <cstdio>
    #include <algorithm>
    #define INF 10000
    using namespace std;
    
    int N, M, status[1250000], seq[100005];
    
    struct Rec
    {
        int x1, x2, y1, y2;
    }e[50];
    
    inline void Getint(int &t)
    {
        char c;
        while (c = getchar(), c < '0' || c > '9') ;
        t = c - '0';
        while (c = getchar(), c >= '0' && c <= '9') {
            t = t * 10 + c - '0';
        }
    }
    
    void dfs(int p, int x1, int y1, int x2, int y2, int sign, int sta)
    {
        if (x1 >= x2 || y1 >= y2) return; // 如果合并区域为零
        if (p == N) {
            if (sta != 0) {
                for (int i = 1; i <= M; ++i) {
                    if ((seq[i] | sta) <= seq[i]) { // 说明当前状态是i的子集
                        status[seq[i]] += sign * (x2 - x1) * (y2 - y1);
                    }
                }
            }
            return;
        }
        dfs(p+1, x1, y1, x2, y2, sign, sta);
        dfs(p+1, max(x1, e[p+1].x1), max(y1, e[p+1].y1), min(x2, e[p+1].x2), min(y2, e[p+1].y2), -sign, sta|(1<<p));
    }
    
    int main()
    {
        int R, c, sta, ca = 0;
        while (scanf("%d %d", &N, &M), N|M) {
            memset(status, 0, sizeof (status));
            for (int i = 1; i <= N; ++i) {
            //    scanf("%d %d %d %d", &e[i].x1, &e[i].y1, &e[i].x2, &e[i].y2);
                Getint(e[i].x1), Getint(e[i].y1), Getint(e[i].x2), Getint(e[i].y2);
            }
            printf("Case %d:\n", ++ca);
            for (int i = 1; i <= M; ++i) {
                sta = 0;
                Getint(R);
                for (int j = 1; j <= R; ++j) {
                    Getint(c);
                    sta |= 1 << (c-1);
                }
                seq[i] = sta;  // 将所有的状态都保留起来
            }
            dfs(0, 0, 0, INF, INF, -1, 0); // 一次dfs求出所有选择下的面积
            for (int i = 1; i <= M; ++i) {
                printf("Query %d: %d\n", i, status[seq[i]]);
            }
            puts("");
        }
        return 0;
    }

    下面是采用扫描线来解决这个问题,首先将所有要询问的矩形的x坐标全部保留起来,从小到大排序,去重,然后枚举每一个小的区域,对要询问的矩形进行区域的高度并,当高度被分割成两个区域时要马上计算出一部分的值,为了防止两块区域再次合并就要求对输出进来的矩形进行y轴的排序,保证下y轴(底边的y轴)是递增序的。

    #include <cstdlib>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define INF 0x3fffffff
    using namespace std;
    
    int N, M, rec[25], line[50], Q;
    
    struct Rectangle
    {
        int x1, y1, x2, y2;
    }e[25];
    
    bool cmpy(int a, int b)
    {
        return e[a].y1 < e[b].y1;
    }
    
    int Merge()
    {
        int cnt = 0, L, R, U, D, sum = 0;
        for (int i = 1; i <= Q; ++i) {
            line[++cnt] = e[rec[i]].x1;
            line[++cnt] = e[rec[i]].x2;
        }
        sort(rec+1, rec+Q+1, cmpy); // 对rec存储的矩形进行y1排序,便于计算高度的并
        sort(line+1, line+1+cnt);
        cnt = unique(line+1, line+1+cnt) - (line + 1);
        for (int i = 2; i <= cnt; ++i) {
            L = line[i-1], R = line[i], U = 0, D = INF;
            for (int j = 1; j <= Q; ++j) {  // 遍历所有的矩形
                int c = rec[j];
                if (e[c].x1 <= L && e[c].x2 >= R) {
                    if (e[c].y1 > U && U > D) {
                        sum += (R - L) * (U - D);
                        U = e[c].y2, D = e[c].y1;
                    }
                    else {
                        U = max(U, e[c].y2);
                        D = min(D, e[c].y1);
                    }
                }
            }
            if (U > D) {
                sum += (R - L) * (U - D);
            }
        }
        return sum;
    }
    
    int main()
    {
        int ca = 0;
        while (scanf("%d %d", &N, &M), N|M) {
            for (int i = 1; i <= N; ++i) {
                scanf("%d %d %d %d", &e[i].x1, &e[i].y1, &e[i].x2, &e[i].y2);
            }
            printf("Case %d:\n", ++ca);
            for (int i = 1; i <= M; ++i) {
                scanf("%d", &Q);
                for (int j = 1; j <= Q; ++j) {
                    scanf("%d", &rec[j]);
                }
                printf("Query %d: %d\n", i, Merge());
            }
            puts("");
        }
        return 0;
    }
  • 相关阅读:
    Opengl编程指南第三章:视图
    OpenGL编程指南第四章:颜色
    OpenGL编程指南第七章:显示列表
    推荐:字体、排版简明入门
    OpenGL编程指南第八章:绘制像素、位图、字体和图像
    推荐:字体、排版简明入门
    OpenGL编程指南第五章:光照
    转载一篇密码学基本介绍
    ARM_异常和中断学习笔记
    ARM指令学习笔记
  • 原文地址:https://www.cnblogs.com/Lyush/p/2613516.html
Copyright © 2011-2022 走看看