离散化的操作

来源:互联网 发布:宾得k3 知乎 编辑:程序博客网 时间:2024/04/30 02:39

主要出现在有double的区间问题(建区间树时)和一些虽然是整型但是范围太大时等(无法保存或者枚举),我们可以发现,范围中很大部分数是相同的,那我们就可以保存区间端点或者范围边界来达到缩小数据的范围。
具体做法是把区间排序去重,然后每两个相邻坐标就能形成新的离散化后的区间。

三道题:
第一题: 221 uva,纯离散化,暴力枚举。
输入俯视图中每个建筑物左下角坐标(即x、y坐标中的最小值)、宽度(x方向长度)、深度(y方向长度)、和高度(以上均为实数)。输出正视图中所有能看到的建筑物的编号,按先x后y从小到大排序。
这里写图片描述
差不多就这个样子

/* * *  Author : Triose *  Email  : Triose@163.com *  Update_time : 2016.6.12 * *///#include<bits/stdc++.h>#include<stdio.h>#include<iostream>#include<string>#include<string.h>#include<algorithm>#include<vector>#include<queue>#include<stack>#include<iterator>#include<math.h>#include<stdlib.h>#include<time.h>#include<map>#include<set>using namespace std;//#define ONLINE_JUDGE#define eps 1e-8#define inf 0x3f3f3f3f#define INF 0x7fffffff#define INFL 0x3f3f3f3f3f3f3f3fLL#define enter putchar(10)#define rep(i,a,b) for(int i = (a); i < (b); ++i)#define repe(i,a,b) for(int i = (a); i <= (b); ++i)#define mem(a,b) (memset((a),b,sizeof(a)))#define sf(a) scanf("%d",&a)#define sfI(a) scanf("%I64d",&a)#define sfd(a,b) scanf("%d%d",&a,&b)#define sft(a,b,c) scanf("%d%d%d",&a,&b,&c)#define sfs(a) scanf("%s",a)#define pf(a) printf("%d\n",a)#define pfd(a,b) printf("%d %d\n",a,b)#define pfP(a) printf("%d %d\n",a.fi,a.se)#define pfs(a) printf("%s\n",a)#define pfI(a) printf("%I64d\n",a)#define PR(a,b) pair<a,b>#define fi first#define se second#define LL long long#define DB double#define ds(t) int t; sf(t)const double PI = acos(-1.0);const double E = exp(1.0);template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }template<class T> inline T Min(T a, T b) { return a < b ? a : b; }template<class T> inline T Max(T a, T b) { return a > b ? a : b; }int n, m;#define N 110struct Building {    DB x, y, w, l, h;    int num;    Building(DB X = 0, DB Y = 0, DB W = 0, DB L = 0, DB H = 0, int num_ = 0) : x(X), y(Y), w(W), l(L), h(H), num(num_) {}    friend bool operator < (const Building & a, const Building & b) {        if(a.x == b.x) return a.y < b.y;        return a.x < b.x;    }    friend istream & operator >> (istream & is, Building & b) {        is >> b.x >> b.y >> b.w >> b.l >> b.h;        return is;    }};Building b[N];DB x[N * 2];bool belong(int i, double pos) {            //判断建筑物是否包含区间中点 等价于包含区间    return b[i].x <= pos && b[i].x + b[i].w >= pos;}bool visible(int i) {                       //判断建筑物是否可见    rep(j, 0, m - 1) {        DB mid = (x[j] + x[j + 1]) / 2;        if(!belong(i, mid)) continue;        bool vs = true;        rep(k, 0, n) {            if(belong(k, mid) && b[k].y < b[i].y && b[k].h >= b[i].h) {                vs = false; break;            }        }        if(vs == true) return true;    }    return false;}void solve(int cs) {    if(cs != 1) enter;    cout << "For map #" << cs << ", the visible buildings are numbered as follows:\n";    cout << b[0].num;    rep(i, 1, n) {        if(visible(i)) {            cout << " " << b[i].num;        }    }    enter;}void Init() {    m = 0;    rep(i, 0, n) {        cin >> b[i];        b[i].num = i + 1;        x[m++] = b[i].x; x[m++] = b[i].x + b[i].w;    }    sort(x, x + m);                             //区间端点排序    m = unique(x, x + m) - x;                   //区间端点去重(必须先排序)    sort(b, b + n);                             //建筑物排序}int main() {#ifndef ONLINE_JUDGE    freopen("in.txt","r",stdin);//  freopen("Out.txt", "w", stdout);#endif    int cas = 0;    while(cin >> n && n) {        Init();        solve(++cas);    }    return 0;}

本来想区间离散化之后构造区间树,然后把建筑物当做区间插入到树中,不过这样不行。因为没法保证判断建筑物b[i]是否可见的时候在建筑物南边的所有b[k]都已经插入到了树中。

poj 2528 Mayor’s posters
离散化 + 区间树

意思不再赘述,大概是有一面无限长的墙,n张海报,按照从左到右从里到外的顺序给出海报的两个端点。问你当海报按照顺序贴上去之后,有多少张海报是没有被完全覆盖的?
和上面那个题很像,不过可以用线段树优化因为输入保证了从左至右从内到外。那么我先贴外面的,再贴里面的(也就是逆着输入顺序贴)就能保证当贴了一张海报并且未被覆盖时,ans 就可以++(因为可能覆盖它的都已经标记在线段树里面了)。另外,由于题目区间范围太大,而n本身不大,需要进行离散化操作。

/* * *  Author : Triose *  Email  : Triose@163.com *  Update_time : 2016.6.12 * *///#include<bits/stdc++.h>#include<stdio.h>#include<iostream>#include<string>#include<string.h>#include<algorithm>#include<vector>#include<queue>#include<stack>#include<iterator>#include<math.h>#include<stdlib.h>#include<time.h>#include<map>#include<set>using namespace std;//#define ONLINE_JUDGE#define eps 1e-8#define inf 0x3f3f3f3f#define INF 0x7fffffff#define INFL 0x3f3f3f3f3f3f3f3fLL#define enter putchar(10)#define rep(i,a,b) for(int i = (a); i < (b); ++i)#define per(i,a,b) for(int i = (a); i >= (b); --i)#define repe(i,a,b) for(int i = (a); i <= (b); ++i)#define mem(a,b) (memset((a),b,sizeof(a)))#define sf(a) scanf("%d",&a)#define sfI(a) scanf("%I64d",&a)#define sfd(a,b) scanf("%d%d",&a,&b)#define sft(a,b,c) scanf("%d%d%d",&a,&b,&c)#define sfs(a) scanf("%s",a)#define pf(a) printf("%d\n",a)#define pfd(a,b) printf("%d %d\n",a,b)#define pfP(a) printf("%d %d\n",a.fi,a.se)#define pfs(a) printf("%s\n",a)#define pfI(a) printf("%I64d\n",a)#define PR(a,b) pair<a,b>#define fi first#define se second#define LL long long#define DB double#define ds(t) int t; sf(t)const double PI = acos(-1.0);const double E = exp(1.0);template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }template<class T> inline T Min(T a, T b) { return a < b ? a : b; }template<class T> inline T Max(T a, T b) { return a > b ? a : b; }int n, m;//最大海报数量#define N 10010//最大区间树节点数量#define M 80010PR(int, int) a[N];          //保存海报int xz[N * 2];              //用于离散化操作int cnt;                    //离散化后点的个数struct Elmt {               //区间树元素    bool uncovered;         //是否未被覆盖    int ls, rs;    Elmt(bool uc = false, int ls_ = 0, int rs_ = 0): uncovered(uc), ls(ls_), rs(rs_) {}    int mid() { return (ls + rs) >> 1; }    void setcov() { uncovered = false; }};Elmt sgt[M];                //区间树//建树操作,并把uncovered 设置成 true(初始化)void Build(int root, int lf, int rg) {      //Build [lf, rg]    sgt[root].uncovered = true;    sgt[root].ls = lf; sgt[root].rs = rg;    if(lf == rg) return ;    int mid = sgt[root].mid();    Build(2 * root + 1, lf, mid);    Build(2 * root + 2, mid + 1, rg);}//读入数据,离散化端点,建树void Init() {    sf(n); cnt = 0;    rep(i, 0, n) {        sfd(a[i].first, a[i].second);        xz[cnt++] = a[i].first;        xz[cnt++] = a[i].second;    }    sort(xz, xz + cnt); cnt = unique(xz, xz + cnt) - xz;    Build(0, 0, cnt - 1);}//插入区间(如果左右端点分别为xz[lf] 和 xz[rg] 的海报插入后发现未被覆盖,返回1,否则返回0)int ins(int root, int lf, int rg) {     //root 是当前根, lf 和 rg 分别是 xz 的下标    if(sgt[root].ls == lf && sgt[root].rs == rg) {        if(sgt[root].uncovered) {       //如果没被覆盖,返回1,贴上海报之后就被覆盖了(维护区间树)            sgt[root].setcov();            return 1;        }        else {                          //如果已经被覆盖,直接返回0            return 0;        }    }    if(!sgt[root].uncovered) return 0;  //如果比它大的区间都已经被覆盖了,就不用更新什么了,直接返回0    int mid = sgt[root].mid();          //以下是区间树的常规写法。注意维护和返回分别该用些什么操作    if(rg <= mid) {        int ans = ins(root * 2 + 1, lf, rg);        sgt[root].uncovered = (sgt[root * 2 + 1].uncovered || sgt[root * 2 + 2].uncovered);        return ans;    }    else if(lf > mid) {        int ans = ins(root * 2 + 2, lf, rg);        sgt[root].uncovered = (sgt[root * 2 + 1].uncovered || sgt[root * 2 + 2].uncovered);        return ans;    }    int ans = ins(root * 2 + 1, lf, mid) | ins(root * 2 + 2, mid + 1, rg);    sgt[root].uncovered = sgt[root * 2 + 1].uncovered || sgt[root * 2 + 2].uncovered;    return ans;}int ID(int* arr, int num, int v) {      //给定海报端点返回再xz中的下标    return lower_bound(arr, arr + num, v) - arr;}int solve() {    int ans = 0;    per(i, n - 1, 0) {        int l = ID(xz, cnt, a[i].first);        int r = ID(xz, cnt, a[i].second);        ans += ins(0, l, r);    }    return ans;}int main() {#ifndef ONLINE_JUDGE    freopen("in.txt","r",stdin);//  freopen("Out.txt", "w", stdout);#endif    ds(t);    while(t--) {        Init();                         //输入        pf(solve());                    //处理    }    return 0;}

还有uva的一道神题,三维空间的离散化。我现在还没懂透!这已经是第五遍尝试了,终于过了!
12171 uva Sculpture

某雕塑由n(n <= 50)个边平行于坐标轴的长方体组成。 每个长方体用6个整数 x0, y0, z0, x, y, z 表示(均小于500大于0),其中 x0 为顶点中坐标最小的一个, x 表示长方体在 x 方向的长度。 其他 4 个值类似定义。 你的任务是统计这个雕像的体积和表面积。 注意, 雕塑内部可能会有密闭空间, 其体积应计算在总体积中,但从“外部”看不到的面不应计入表面积。 雕塑可能会由多个连通块组成。

直接给代码吧。懒得分析了。留个位置下次分析。

/* * *  Author : Triose *  Email  : Triose@163.com *  Update_time : 2016.6.12 * *///#include<bits/stdc++.h>#include<stdio.h>#include<iostream>#include<string>#include<string.h>#include<algorithm>#include<vector>#include<queue>#include<stack>#include<iterator>#include<math.h>#include<stdlib.h>#include<time.h>#include<map>#include<set>using namespace std;//#define ONLINE_JUDGE#define eps 1e-8#define inf 0x3f3f3f3f#define INF 0x7fffffff#define INFL 0x3f3f3f3f3f3f3f3fLL#define enter putchar(10)#define rep(i,a,b) for(int i = (a); i < (b); ++i)#define per(i,a,b) for(int i = (a); i >= (b); --i)#define repe(i,a,b) for(int i = (a); i <= (b); ++i)#define mem(a,b) (memset((a),b,sizeof(a)))#define sf(a) scanf("%d",&a)#define sfI(a) scanf("%I64d",&a)#define sfd(a,b) scanf("%d%d",&a,&b)#define sft(a,b,c) scanf("%d%d%d",&a,&b,&c)#define sfs(a) scanf("%s",a)#define pf(a) printf("%d\n",a)#define pfd(a,b) printf("%d %d\n",a,b)#define pfP(a) printf("%d %d\n",a.fi,a.se)#define pfs(a) printf("%s\n",a)#define pfI(a) printf("%I64d\n",a)#define PR(a,b) pair<a,b>#define fi first#define se second#define LL long long#define DB double#define ds(t) int t; sf(t)const double PI = acos(-1.0);const double E = exp(1.0);template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }template<class T> inline T Min(T a, T b) { return a < b ? a : b; }template<class T> inline T Max(T a, T b) { return a > b ? a : b; }int n, m;const int maxn = 60;const int maxc = 1001;int x[maxn], y[maxn], z[maxn], dx[maxn], dy[maxn], dz[maxn];//原始数据int spc[maxn * 2][maxn * 2][maxn * 2];                      //三维空间const int dirs[6][3] = {{1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1}};                                                       //方向//用于离散化int xz[maxn * 2], yz[maxn * 2], zz[maxn * 2];int xcnt, ycnt, zcnt;//表示空间中离散化后的一个点(也就是三条边)(离散化后一个点表示一条边)struct Cub {    int x, y, z;    Cub(int x_ = 0, int y_ = 0, int z_ = 0): x(x_), y(y_), z(z_) {}    void setvis() const { spc[x][y][z] = -1; }    bool canvis() const { return spc[x][y][z] != -1;}    bool valid() const { return x < xcnt - 1 && y < ycnt - 1 && z < zcnt - 1 && x >= 0 && y >= 0 && z >= 0; }    bool isboundary() const { return spc[x][y][z] == 1; }    Cub & neighbor(int i) const { return * (new Cub(x + dirs[i][0], y + dirs[i][1], z + dirs[i][2])); }    int volume() const {return (xz[x + 1] - xz[x]) * (yz[y + 1] - yz[y]) * (zz[z + 1] - zz[z]); }    int surface(int i) const {        int xlen = xz[x + 1] - xz[x], ylen = yz[y + 1] - yz[y], zlen = zz[z + 1] - zz[z];        if(dirs[i][0] != 0) return ylen * zlen;        else if(dirs[i][1] != 0) return xlen * zlen;        return xlen * ylen;    }};void standerlize(int * arr, int & cnt) {//离散化过程    sort(arr, arr + cnt);    cnt = unique(arr, arr + cnt) - arr;}int ID(int * arr, int cnt, int v) {     //找位置    return lower_bound(arr, arr + cnt, v) - arr;}void mark() {                           //标记边    rep(i, 0, n) {        int X1 = ID(xz, xcnt, x[i]), X2 = ID(xz, xcnt, dx[i]);        int Y1 = ID(yz, ycnt, y[i]), Y2 = ID(yz, ycnt, dy[i]);        int Z1 = ID(zz, zcnt, z[i]), Z2 = ID(zz, zcnt, dz[i]);        rep(X, X1, X2) rep(Y, Y1, Y2) rep(Z, Z1, Z2) spc[X][Y][Z] = 1;    }}void Init() {                           //初始化    mem(spc, 0); xcnt = 2; ycnt = 2; zcnt = 2;    xz[0] = 0; xz[1] = maxc;    yz[0] = 0; yz[1] = maxc;    zz[0] = 0; zz[1] = maxc;    cin >> n;    rep(i, 0, n) {        cin >> x[i] >> y[i] >> z[i] >> dx[i] >> dy[i] >> dz[i];        dx[i] += x[i]; dy[i] += y[i]; dz[i] += z[i];        xz[xcnt++] = x[i]; xz[xcnt++] = dx[i];        yz[ycnt++] = y[i]; yz[ycnt++] = dy[i];        zz[zcnt++] = z[i]; zz[zcnt++] = dz[i];    }    standerlize(xz, xcnt);    standerlize(yz, ycnt);    standerlize(zz, zcnt);    mark();}void floodfill(int & s, int & v) {  //种子填充的bfs写法    int maxv = maxc * maxc * maxc;    queue<Cub> q;    Cub p, nxt; q.push(p); p.setvis();    while(!q.empty()) {        p = q.front(); q.pop();        v += p.volume();        rep(i, 0, 6) {            nxt = p.neighbor(i);            if(!nxt.valid()) continue;            if(nxt.isboundary()) {                s += nxt.surface(i);                continue;            }            if(nxt.canvis()) {                nxt.setvis();                q.push(nxt);            }        }    }    v = maxv - v;}int main() {#ifndef ONLINE_JUDGE    freopen("in.txt","r",stdin);//  freopen("Out.txt", "w", stdout);#endif    ios::sync_with_stdio(false);    int t; cin >> t;    while(t--) {        Init();        int v = 0, s = 0;        floodfill(s, v);        cout << s << " " << v << endl;    }    return 0;}

这题难在离散化之后一个点表示一条边,本来就是三维的空间,相当于再增加了一个维度,我想象不出来。。。

总结:离散化可以降维度,上题就是把边降成了点,只不过点还是三维的点。但是第二题就确确实实把区间变成了一个点。反正离散化能够把连续的范围大的问题,转换成离散的范围小的问题。

0 0