Voronoi图和Delaunay三角剖分
刷题的时候发现了这么一个新的东西:Voronoi图和Delaunay三角剖分
发现这个东西可以$O(nlogn)$解决平面图最小生成树问题感觉非常棒
然后就去学了..
看的n+e的blog,感谢n+e的耐心教导..
Voronoi图是个啥
Delaunay三角剖分
最优三角剖分就是使每一个三角形的外接圆都不包含其他的点的三角剖分
这个算法就是求最优三角剖分的
简单来说就是分治合并
对于点数小于等于$3$的可以直接连边
合并的时候
1)先找到两边最下面的点,这个可以用凸包求,然后连边
2)对于现在得到的两个点$p_1$、$p_2$,找到一个点连接着$p_1$且由这三个点的外接圆不包含别的任何点,并删除这个外接圆经过的边,$p_2$也是如此
3)看现在找出来的两个点$y_1$、$y_2$,找其中一个点使得它与$p_1$、$p_2$的外接圆不包含另外一个点,使其与对应的点连边
4)重复(2)(3)直到无边可连
对n+e代码的修改
一开始压根不知道怎么实现这个算法的时候去看了看n+e的代码..
发现他的代码中每次都要遍历$p_1$和$p_2$的所有边,这样的做法在特殊的图里面是$O(n^2)$的
可以说他自己出的数据偏水??(雾
然后看了上面那篇详细的文章,发现它的边是按照顺时针或者逆时针的方向进行取边的,这样遇到的边要不删掉要不就是最优的
所以我开了一个双向链表来储存边,使得边是按照$(-\dfrac{\pi}{2},\dfrac{3\pi}{2}]$顺时针排列
然后就会发现细节一大堆..
调了我3天的东西就直接放上来好了
判断一个点是否在某三个点的外接圆内
在这篇blog里面有讲,但是貌似图都爆掉了??
就是把点映射到$z=x^2+y^2$的抛物面上,这三个点就会形成一个新的平面,然后再判断剩下的那个点和这个平面的关系即可
下面放几张n+e给我的图片方便理解?
抛物面$z=x^2+y^2$
其实第三张图应该很清晰了吧..
在圆内的点都会在平面下方,而在圆外的都会在平面上方
这个用三维叉积点积判一下就好了
Code
下面就贴一下我3天的成果吧..
还有什么问题请指教..
bzoj4219
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <vector>
#define eps 1e-10
using namespace std;
const int Maxn = 100010;
double _max(double x, double y) { return x > y ? x : y; }
double _min(double x, double y) { return x < y ? x : y; }
struct node {
int y, nxt, frt, opp;
}a[Maxn<<3]; int first[Maxn], last[Maxn], num[Maxn<<3], now;
int sta[Maxn], tp;
int pb(int x, int k, int y) {
int u = num[now++];
a[u].y = y; a[u].frt = k;
if(k){
a[u].nxt = a[k].nxt;
if(a[k].nxt) a[a[k].nxt].frt = u;
else last[x] = u;
a[k].nxt = u;
} else {
if(first[x]) a[first[x]].frt = u;
else last[x] = u;
a[u].nxt = first[x]; first[x] = u;
}
return u;
}
int pf(int x, int k, int y) {
int u = num[now++];
a[u].y = y; a[u].nxt = k;
if(k){
a[u].frt = a[k].frt;
if(a[k].frt) a[a[k].frt].nxt = u;
else first[x] = u;
a[k].frt = u;
} else {
if(last[x]) a[last[x]].nxt = u;
else first[x] = u;
a[u].frt = last[x]; last[x] = u;
}
return u;
}
void del(int x, int k) {
num[--now] = k;
if(a[k].nxt) a[a[k].nxt].frt = a[k].frt;
else last[x] = a[k].frt;
if(a[k].frt) a[a[k].frt].nxt = a[k].nxt;
else first[x] = a[k].nxt;
}
double _abs(double x) { return x < 0 ? -x : x; }
int zero(double x) { return _abs(x) < eps ? 1 : 0; }
struct Point {
double x, y;
Point(double x = 0, double y = 0) : x(x), y(y) {}
bool operator<(const Point &A) const { return zero(x-A.x) ? y < A.y : x < A.x; }
Point operator-(const Point &A) const { return Point(x-A.x, y-A.y); }
}list[Maxn]; int n; double X, Y;
double Cross(Point A, Point B) { return A.x*B.y-B.x*A.y; }
double Dot(Point A, Point B) { return A.x*B.x+A.y*B.y; }
double dis(Point A) { return sqrt(Dot(A, A)); }
double dis(int x, int y) { return dis(list[y]-list[x]); }
struct Point3 {
double x, y, z;
Point3(double x = 0, double y = 0, double z = 0) : x(x), y(y), z(z) {}
Point3 operator-(const Point3 &A) const { return Point3(x-A.x, y-A.y, z-A.z); }
};
double Dot(Point3 A, Point3 B) { return A.x*B.x+A.y*B.y+A.z*B.z; }
Point3 Cross(Point3 A, Point3 B) { return Point3(A.y*B.z-A.z*B.y, A.z*B.x-A.x*B.z, A.x*B.y-A.y*B.x); }
Point3 t(Point A) { return Point3(A.x, A.y, A.x*A.x+A.y*A.y); }
bool incir(Point D, Point A, Point B, Point C) {
if(Cross(B-A, C-A) < -eps) swap(B, C);
Point3 aa = t(A), bb = t(B), cc = t(C), dd = t(D);
return Dot(Cross(bb-aa, cc-aa), dd-aa) < -eps;
}
bool incir(int D, int A, int B, int C) { return incir(list[D], list[A], list[B], list[C]); }
void divi(int L, int R) {
if(L == R) return;
if(L+1 == R){
int k1 = pb(L, 0, R); int k2 = pf(R, 0, L);
a[k1].opp = k2; a[k2].opp = k1;
return;
}
int mid = L+R>>1, i, j, k;
divi(L, mid); divi(mid+1, R);
int p1 = 0, p2 = 0; tp = 0;
for(i = L; i <= R; i++){
while(tp > 1 && Cross(list[i]-list[sta[tp-1]], list[sta[tp]]-list[sta[tp-1]]) > eps) tp--;
sta[++tp] = i;
}
for(i = 1; i < tp; i++) if(sta[i] <= mid && sta[i+1] > mid){ p1 = sta[i]; p2 = sta[i+1]; break; }
int kp1, kp2;
for(kp1 = last[p1]; kp1; kp1 = a[kp1].frt){
if(Cross(list[a[kp1].y]-list[p1], list[p2]-list[p1]) < eps || Cross(list[a[kp1].y]-list[p1], Point(0,1)) < -eps) break;
}
int k1 = pb(p1, kp1, p2);
for(kp2 = first[p2]; kp2; kp2 = a[kp2].nxt){
if(Cross(list[a[kp2].y]-list[p2], list[p1]-list[p2]) > -eps || Cross(list[a[kp2].y]-list[p2], Point(0,1)) > eps) break;
}
int k2 = pf(p2, kp2, p1);
a[k1].opp = k2; a[k2].opp = k1;
while(1){
int np1 = 0, np2 = 0;
for(; kp1; kp1 = a[kp1].frt){
if(Cross(list[a[kp1].y]-list[p1], list[p2]-list[p1]) > -eps){
if(Cross(list[a[kp1].y]-list[p1], Point(0,1)) > -eps) continue;
else break;
}
if(a[kp1].frt && incir(a[a[kp1].frt].y, p1, p2, a[kp1].y)) del(a[kp1].y, a[kp1].opp), del(p1, kp1);
else { np1 = kp1; break; }
}
for(; kp2; kp2 = a[kp2].nxt){
if(Cross(list[a[kp2].y]-list[p2], list[p1]-list[p2]) < eps){
if(Cross(list[a[kp2].y]-list[p2], Point(0,1)) < -eps) continue;
else break;
}
if(a[kp2].nxt && incir(a[a[kp2].nxt].y, p1, p2, a[kp2].y)) del(a[kp2].y, a[kp2].opp), del(p2, kp2);
else { np2 = kp2; break; }
}
if(!np1 && !np2) break;
if(!np2 || (np1 && !incir(a[kp2].y, p1, p2, a[kp1].y))){
p1 = a[kp1].y;
k2 = pf(p2, kp2, p1);
for(kp1 = last[p1]; kp1; kp1 = a[kp1].frt){
if(Cross(list[a[kp1].y]-list[p1], list[p2]-list[p1]) < eps || Cross(list[a[kp1].y]-list[p1], Point(0,1)) < -eps) break;
}
k1 = pb(p1, kp1, p2);
a[k1].opp = k2; a[k2].opp = k1;
} else {
p2 = a[kp2].y;
k1 = pb(p1, kp1, p2);
for(kp2 = first[p2]; kp2; kp2 = a[kp2].nxt){
if(Cross(list[a[kp2].y]-list[p2], list[p1]-list[p2]) > -eps || Cross(list[a[kp2].y]-list[p2], Point(0,1)) > eps) break;
}
k2 = pf(p2, kp2, p1);
a[k1].opp = k2; a[k2].opp = k1;
}
}
}
struct enode {
int x, y; double d;
enode(int x = 0, int y = 0, double d = 0) : x(x), y(y), d(d) {}
bool operator<(const enode &A) const { return d < A.d; }
}e[Maxn<<4]; int el;
int fa[Maxn];
int ff(int x) { return fa[x] == x ? x : fa[x] = ff(fa[x]); }
int main() {
int i, j, k;
scanf("%d%lf%lf", &n, &X, &Y);
for(i = 1; i <= n; i++) scanf("%lf%lf", &list[i].x, &list[i].y);
now = 1;
for(i = 1; i <= n<<3; i++) num[i] = i;
sort(list+1, list+n+1);
divi(1, n);
for(i = 1; i <= n; i++){
e[++el] = enode(i, n+1, _min(list[i].x, Y-list[i].y));
e[++el] = enode(i, n+2, _min(list[i].y, X-list[i].x));
for(k = first[i]; k; k = a[k].nxt) e[++el] = enode(i, a[k].y, dis(i, a[k].y)/2.0);
}
sort(e+1, e+el+1);
for(i = 1; i <= n+2; i++) fa[i] = i;
for(i = 1; i <= el; i++){
int fx = ff(e[i].x), fy = ff(e[i].y);
if(fx != fy){
fa[fx] = fy;
if(ff(n+1) == ff(n+2)){ printf("%lf\n", e[i].d); return 0; }
}
}
return 0;
}
小总结??
虽然这次搞这个东西用的时间很长.. 但是收获还是很大的..
最后差那么几个点wa还是很想放弃的..
但是还是坚持下了来了嘛..
加油啊..
Voronoi图和Delaunay三角剖分的更多相关文章
- Voronoi图与Delaunay三角剖分
详情请见[ZJOI2018]保镖 题解随笔 - 99 文章 - 0 评论 - 112
- OpenCV生成点集的Delaunay剖分和Voronoi图
实现内容: 设置一副图像大小为600*600.图像像素值全为0,为黑色. 在图像中Rect(100,100,400,400)的区域随机产生20个点.并画出. 产生这些点集的Delaunay剖分和Vor ...
- paper 153:Delaunay三角剖分算法--get 这个小技术吧!
直接摘自百度百科,希望大家能根据下面的介绍稍微理顺思路,按需使用,加油! 解释一下:点集的三角剖分(Triangulation),对数值分析(比如有限元分析)以及图形学来说,都是极为重要的一项预处理技 ...
- Delaunay三角剖分及MATLAB实例
https://blog.csdn.net/piaoxuezhong/article/details/68065170 一.原理部分 点集的三角剖分(Triangulation),对数值分析(如有限元 ...
- Voronoi图及matlab实现
[题外话:想一想真是...美赛时我预测求爱尔兰的充电站位置分布,画Voronoi图,程序跑了一个小时...] Voronoi图,又叫泰森多边形或Dirichlet图,它是由一组由连接两邻点直线的垂 ...
- 渲染voronoi图
渲染voronoi图要比计算voronoi图简单. 渲染voronoi图: 方法1: 在pixel shader里,对每一个像素,求哪个种子点到它的距离最近,将此种子点的颜色作为此像素颜色. 当种子点 ...
- Arcgis做出voronoi图
人类第一步,,,我需要给我目前的基站点数据划分voronoi,预期得到每个基站的服务范围 在地统计模块geostatistical analysis 下面的数据探索expore就有Voronoi图 将 ...
- 《图像处理实例》 之 Voronoi 图
Voronoi 图的设计 以下的改进是http://www.imagepy.org/的作者原创,我只是对其理解之后改进和说明,欢迎大家使用这个小软件! 如有朋友需要源工程,请在评论处留邮箱! 说明:类 ...
- C++ 生成 voronoi 图 & C++生成泰森多边形图形
1. 功能 生成voronoi图的一个类 2. 代码 VoronoiDiagramGenerator.h #pragma once //Microsoft Visual Studio 2015 Ent ...
随机推荐
- 金融量化分析【day112】:双均线策略
一.双均线策略 1.什么是双均线策略? 2.实现代码 def initialize(context): set_benchmark('601318.XSHG') set_option('use_rea ...
- SQLALlchemy数据查询小集合
SQLAlchemy是Python编程语言下的一款ORM框架,该框架建立在数据库API之上,使用关系对象映射进行数据库操作.将对象转换成SQL,然后使用数据API执行SQL并获取执行结果.在写项目的过 ...
- JGUI源码:从头开始,建一个自己的UI框架(1)
开篇 1.JGUI是为了逼迫自己研究底层点的前端技术而做的框架,之前对web底层实现一直没有深入研究,有了技术瓶颈,痛定思痛从头研究, 2.虽然现在vue技术比较火,但还在发展阶段,暂时先使用JQue ...
- 【js课设】电子画板01
这学期web开发课的课设选了电子画板课题.(人家本来想做富文本编辑器的嘛然鹅老师在第二版里把这题删掉了。゚ヽ(゚´Д`)ノ゚。) 主要考虑的有[界面美观][画笔类型][画布分层]这三个点了. [界面美 ...
- densenet 中的shortcut connection
DenseNet 在FCN 网络中考虑加入skip connection,在resnet 中加入identity 映射,这些 shortcut connection等结构能够得到更好的检测效果,在d ...
- (五)Java工程化--Jenkins
Jenkins简介 Jenkins 是一种用Java语言实现的持续集成工具,Jenkins是一个平台, 在此基础上实现下面两个目的. CI 持续集成(Continous Integration) CD ...
- TP5报错
Array to string conversion 数组不能用echo来输出,可使用var_dump().dump()或print_r()
- C#+EntityFramework编程方式详细之Model First
Model First Model First模式即“模型优先”,这里的模型指的是“ADO.NET Entity Framework Data Model”,此时你的应用并没有设计相关数据库,在VS中 ...
- Java_Runtime&Process&ProcessBuilder
目录 一.Runtime类 二.Process类 三.ProcessBuilder类 在Java中想调用外部程序,或者执行命令和可运行文件时,网上的典型实例一般都是通过Runtime.getTime( ...
- 进入js
JavaScript概述 ECMAScript和JavaScript的关系 1996年11月,JavaScript的创造者--Netscape公司,决定将JavaScript提交给国际标准化组织ECM ...