【第21期】观点:人工智能到底用 GPU?还是用 FPGA?

离散化的操作

原创 2016年08月29日 10:58:01

主要出现在有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;
}

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

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

版权声明:本文为博主原创文章,未经博主允许不得转载。 举报

相关文章推荐

hdu 3333 离散化+离线操作

若不是题中的数看错了导致舒数组开小造成的RE,就是1A呀,话说最近总是犯这种错误,难道要换眼镜? 一看肯定是线段树,求区间内不同的数的和,由于这些数要不同,可能不是连续的,所以要离线处理。。将访问操...

POJ 3680 Intervals(离散化+费用流)

POJ 3680 Intervals(离散化+费用流) http://poj.org/problem?id=3680 <span style="col

POJ 1151 Atlantis(重叠矩阵面积和=离散化)

POJ 1151 Atlantis(重叠矩阵面积和=离散化) http://poj.org/problem?id=1151 <span style="

poj 1151 线段树+离散化+扫描线 矩形面积并 (矩阵操作类)

这道题跟1177 求覆盖矩阵周长是一个思路的题目。题意: 给出 若干矩形。然后求总共被覆盖的面积。算法 : 用线段树 储存 y 轴 的坐标 。然后由于考虑到是小数。所以离散化成为整数。        ...

HDU 3650 Hot Expo(线段覆盖==离散化)

HDU 3650 Hot Expo(线段覆盖==离散化) http://acm.hdu.edu.cn/showproblem.php?pid=3650 <span style="f

线段树—离散化压缩空间优化

应用离散化缩小线段树的空间。例题:poj 2528 Mayor's posters。

[离散化+蛮力]POJ_1151_Atlantis

题意:求矩形面积并 分析:本来是要学习扫描线的,不过还没看懂。。囧。。在看了黑书之后,发现这题数据规模如此小(100个矩形),于是YY出了一种方法: 1.首先把X,Y坐标都进行离散化。 2.离散化之后,将整个平面划分成很多面积不等的小矩形。 3.枚举每个大矩形,得到大矩形离散化后的左上角点和右下角点的位置。 4.把每个小矩形标记为c[i][j]表示第i行第j列个矩形是否已经被计算过,那么在大矩形中枚举其包含的所有小矩形,如果小矩形未被包含着总面积增加小矩形的面积,并标记该小矩形。 </

poj 2299 树状数组+离散化 or 归并排序 求逆序对

题意: 给一个n 求逆序对。 解析: 树状数组+离散化是一种方法。 归并排序是一种方法。 线段树+离散化应该也ok。 树状数组: 离散化完了以后:(以下转载) 假设...

关于连续值离散化[MODL]

将连续值离散化的问题,在数据挖掘和机器学习的任务中并不鲜见,当然离散化的方法也有很多。 本文将要介绍的是一种基于数据标签(label)来对连续数据值做离散化分割的监督学习方法。   问题: <p style="font

poj 2299 Ultra-QuickSort(树状数组求逆序数+离散化)

树状数组,具体的说是 离散化+树状数组。这也是学习树状数组的第一题. 算法的大体流程就是: 1.先对输入的数组离散化,使得各个元素比较接近,而不是离散的, 2.接着,运用树状数组的标准操作来累...
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)