离散化的操作
来源:互联网 发布:宾得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;}
这题难在离散化之后一个点表示一条边,本来就是三维的空间,相当于再增加了一个维度,我想象不出来。。。
总结:离散化可以降维度,上题就是把边降成了点,只不过点还是三维的点。但是第二题就确确实实把区间变成了一个点。反正离散化能够把连续的范围大的问题,转换成离散的范围小的问题。
- 离散化的操作
- 离散化的基本操作
- 离散化操作
- POJ 1418 圆的基本操作以及 圆弧离散化
- 数据的离散化
- 数据的离散化
- 数据的离散化
- 数据的离散化
- 数据的离散化
- 数据的离散化
- 数据的离散化
- 序列的 离散化
- 坐标的离散化
- 数据的离散化
- 数组的离散化
- hdu 3333 离散化+离线操作
- 线段树+离散化操作(poj 2528)
- 一些去重离散化操作
- 搭建kafaka的环境
- 51Nod 算法马拉松17 路径计数 莫比乌斯函数加暴力构图
- 95. Unique Binary Search Trees II
- Makefile经典教程
- Restful 应用分析
- 离散化的操作
- Android编译命令m, mm, mmm
- try...catch...finally的陷阱——加锁的线程开发经验分享
- RTMP 协议
- 运行.exe文件并获取返回值(使用WinExec和system的区别)
- MyEclipse10激活详解
- 写出高效清晰Layout布局文件的一些技巧
- Unity 分隔条 Spliter
- PHP7运行环境搭建(Windows7)