离散化的操作

主要出现在有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 110
struct 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 80010

PR(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;
}

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值