線分樹の学習:どのように線分樹で矩形面積を計算しますか?
30646 ワード
線分樹は、離散的かつ線形的な思想にまたがる柔軟な区間管理機能を持つデータ構造です.
私たちが一番よく使う線分樹は区間求和です.
でも、普通は線分の木を使っています.こんなに真っ直ぐに使うことはできません.怠惰な操作は普通どこで使いますか?区間の更新など!区間を更新する時、私達は普通は直接馬鹿に行ってすべての点を更新するのではなくて、更新したい区間を先に記録して、それから区間のサブエリアを使う時、私達はやっと本当の更新に行きます.
線分樹はデータ構造ですが、データ構造は私達の生活の中で現実的なものが極めて抽象的な結晶です.私たちがやるべきことは、物事を分析し、現象を通して本質を見ることです.この点は話しやすいですが、やってみると難しいです.
難しいですが、やはりやります.それは線分樹の幾何学的応用である.もっと直接に話してください.
どのように線分を線分の木で表しますか?例えば線分[1,5]は線分樹でどう表しますか?それとも前の木のように:[1,1][2,2]、[3,3]、[4,5]ですか?それはだめです
私たちはこのように木を建てました.[1,2][2,3][3,4][4,5]の中には元の線分の概念が含まれています.
また、線分の長さが大きい場合や、線分が浮動小数点型のデータであれば、線分を離散化する必要があります.離散化の方法も簡単です.例えば、三本の線分があります.1.1-2.2 3.3-1.5 8.8-100.1私たちは長さ6の配列で保存できます.そして、下のマーカーにkeyを加えてツリーを作ります.これで線分が一つ離散化します.
考えが分析されたら、一つの問題で悟りを深めることができます.線分樹の水問題poj 1151
ブログを参照してください:http://blog.csdn.net/tsaid/article/details/6706893
私たちが一番よく使う線分樹は区間求和です.
- /*
- * InterverTree.cpp
- *
- * Created on: 2012-11-1
- * Author: Administrator
- */
- #include
- #define M 100
- #define Mid(a,b) ((a)+(b))>>1
- int a[11]={0,1,2,3,4,5,6,7,8,9,10};
- struct Tree{
- int left,right;
- int key,sum;
- }tree[M];
- /*
- *
- * */
- void build(int id,int left,int right){
- tree[id].left=left;
- tree[id].right=right;
- if(left==right){
- tree[id].sum=tree[id].key=a[left];
- return;
- }
- int mid=Mid(left,right);
- build(id*2,left,mid);
- build(id*2+1,mid+1,right);
- tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
- }
- /*
- *
- * */
- void update(int id,int pos,int key){
- if(tree[id].left==tree[id].right){
- tree[id].sum=tree[id].key=key;
- return ;
- }
- int mid=Mid(tree[id].left,tree[id].right);
-
- pos<=mid?update(id*2,pos,key):update(id*2+1,pos,key);
-
- tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
- }
- /*
- *
- * */
- int query(int id,int left,int right){
- if(tree[id].left==left&&tree[id].right==right){
- return tree[id].sum;
- }
- int mid=Mid(tree[id].left,tree[id].right);
-
- if(right<=mid){
- return query(id*2,left,right);
- }else if(left>mid){
- return query(id*2+1,left,right);
- }else{
- return query(id*2,left,mid)+query(id*2+1,mid+1,right);
- }
- }
- int main(){
- build(1,1,10);// a ,0
- int ans=query(1,1,5);// [1,5]
- printf("sum=%d
",ans);
- update(1,5,0);// 5
- ans=query(1,1,10);
- printf("sum=%d
",ans);
- return 0;
- }
上のように一番基礎的な線分樹です.典型的に空間で時間を変える方法です.また、線分ツリーは、ツリー型のデータ構造の慣行の特徴を継承しています.クエリの複雑さをO(n)からロゴ(n)に低減します.でも、普通は線分の木を使っています.こんなに真っ直ぐに使うことはできません.怠惰な操作は普通どこで使いますか?区間の更新など!区間を更新する時、私達は普通は直接馬鹿に行ってすべての点を更新するのではなくて、更新したい区間を先に記録して、それから区間のサブエリアを使う時、私達はやっと本当の更新に行きます.
- /*
- * InterverTree.cpp
- *
- * Created on: 2012-11-1
- * Author: Administrator
- */
- #include
- #define M 100
- #define Mid(a,b) ((a)+(b))>>1
- int a[11]={0,1,2,3,4,5,6,7,8,9,10};
- struct Tree{
- int left,right;
- int sum,add;
- }tree[M];
- /*
- *
- * */
- void build(int id,int left,int right){
- tree[id].left=left;
- tree[id].right=right;
- if(left==right){
- tree[id].sum=a[left];
- tree[id].add=0;
- return;
- }
- int mid=Mid(left,right);
- build(id*2,left,mid);
- build(id*2+1,mid+1,right);
- tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
- }
-
- void push_down(int id){
- if(tree[id].add>0){
- tree[id].sum+=(tree[id].right-tree[id].left+1)*tree[id].add;
- if(tree[id].left
//
- tree[id*2].add+=tree[id].add;
- tree[id*2+1].add+=tree[id].add;
- }
- tree[id].add=0;
- }
- }
- /*
- *
- * */
- void update(int id,int pos,int key){
- push_down(id);
- if(tree[id].left==tree[id].right){
- tree[id].sum=key;
- return ;
- }
- int mid=Mid(tree[id].left,tree[id].right);
-
- pos<=mid?update(id*2,pos,key):update(id*2+1,pos,key);
-
- tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
- }
- /*
- * : [left,right] key
- * */
- void update_add(int id,int left,int right,int key){
- push_down(id);
- if(tree[id].left==left&&tree[id].right==right){//
- tree[id].add=key;
- return;
- }
- int mid=Mid(tree[id].left,tree[id].right);
- if(right<=mid){
- update_add(id*2,left,right,key);
- }else if(left>mid){
- update_add(id*2+1,left,right,key);
- }else{
- update_add(id*2,left,mid,key);
- update_add(id*2+1,mid+1,right,key);
- }
- }
- /*
- *
- * */
- int query(int id,int left,int right){
- push_down(id);
- if(tree[id].left==left&&tree[id].right==right){
- return tree[id].sum;
- }
- int mid=Mid(tree[id].left,tree[id].right);
- if(right<=mid){
- return query(id*2,left,right);
- }else if(left>mid){
- return query(id*2+1,left,right);
- }else{
- return query(id*2,left,mid)+query(id*2+1,mid+1,right);
- }
- }
- int main(){
- build(1,1,10);// a ,0
- int ans=query(1,1,5);// [1,5]
- printf("sum=%d
",ans);
- update(1,6,10);
- //
- update_add(1,1,5,10);
- ans=query(1,1,6);
- printf("ans=%d
",ans);
- return 0;
- }
これも線分樹の基礎操作であり、一般的には初歩的な線分樹は誤った領域に入りやすいです.つまり線分樹は配列だけを操作することができます.線分樹建樹も必ずbuild(id*2、left、mid)、build(id*2+1、mid+1、right)です.このように線分樹を理解すると、大間違いです.線分樹はデータ構造ですが、データ構造は私達の生活の中で現実的なものが極めて抽象的な結晶です.私たちがやるべきことは、物事を分析し、現象を通して本質を見ることです.この点は話しやすいですが、やってみると難しいです.
難しいですが、やはりやります.それは線分樹の幾何学的応用である.もっと直接に話してください.
どのように線分を線分の木で表しますか?例えば線分[1,5]は線分樹でどう表しますか?それとも前の木のように:[1,1][2,2]、[3,3]、[4,5]ですか?それはだめです
私たちはこのように木を建てました.[1,2][2,3][3,4][4,5]の中には元の線分の概念が含まれています.
また、線分の長さが大きい場合や、線分が浮動小数点型のデータであれば、線分を離散化する必要があります.離散化の方法も簡単です.例えば、三本の線分があります.1.1-2.2 3.3-1.5 8.8-100.1私たちは長さ6の配列で保存できます.そして、下のマーカーにkeyを加えてツリーを作ります.これで線分が一つ離散化します.
考えが分析されたら、一つの問題で悟りを深めることができます.線分樹の水問題poj 1151
ブログを参照してください:http://blog.csdn.net/tsaid/article/details/6706893
- #include
- #include
- using namespace std;
- #define M 1000
- #define eps 1e-8
- struct Tree{
- int ll,rr,cover;// cover int bool
- double lf,rf;
- double len;
- }tree[4*M];
- struct Line{
- double x,y1,y2;
- int flag;
- }line[M];
- double y[M];// y
- int cmp_double(const double &d1,const double &d2){
- double d=d1-d2;
- if(d>eps)//d1>d2
- return 1;
- else if(d//d1
- return -1;
- else //d1==d2
- return 0;
- }
- //
- bool cmp(const Line &l1,const Line &l2){//
- return l1.x
- }
- //
- void build(int id,int ll,int rr){
- tree[id].ll=ll;tree[id].rr=rr;
- tree[id].lf=y[ll],tree[id].rf=y[rr];
- tree[id].len=tree[id].cover=0;
- if(ll+1==rr)return;// ,
- int mid=(ll+rr)>>1;
- build(id*2,ll,mid);
- build(id*2+1,mid,rr);
- }
- void lenght(int id){//
- if(tree[id].cover>0){
- tree[id].len=tree[id].rf-tree[id].lf;
- }else if(tree[id].ll+1==tree[id].rr){//
- tree[id].len=0;
- }else
- tree[id].len=tree[id*2].len+tree[id*2+1].len;
- }
- //
- void update(int id,Line line){
- if(cmp_double(tree[id].lf,line.y1)==0&&cmp_double(tree[id].rf,line.y2)==0){
- tree[id].cover+=line.flag;
- lenght(id);
- return;
- // , ,
- // , mid
-
- }else if(cmp_double(line.y1,tree[2*id+1].lf)>=0){//
- update(id*2+1,line);
- }else if(cmp_double(line.y2,tree[id*2].rf)<=0){//
- update(id*2,line);
- }else{//
- Line tmp;
- tmp=line;tmp.y2=tree[id*2].rf;
- update(2*id,tmp);
- tmp=line;tmp.y1=tree[id*2+1].lf;
- update(2*id+1,tmp);
- }
- lenght(id);// , len
- }
- int main(){
- int n;
- int i,t,ti=0;
- while(scanf("%d",&n)&&n){
- double x1,y1,x2,y2;
- for(t=i=1;i<=n;i++){
- scanf("%lf %lf %lf %lf",&x1,&y1,&x2,&y2);
- line[t].x=x1;
- line[t].y1=y1;
- line[t].y2=y2;
- line[t].flag=1;
- y[t]=y1;
- t++;
- line[t].x=x2;
- line[t].y1=y1;
- line[t].y2=y2;
- line[t].flag=-1;
- y[t]=y2;
- t++;
- }
- sort(line+1,line+t,cmp);
- sort(y+1,y+t);//
-
- build(1,1,t-1);
- update ( 1, line[1] );//
- double ans=0.00;
- for(i=2;i
- ans+=tree[1].len*(line[i].x-line[i-1].x);
- update(1,line[i]);// ,
- }
- printf("Test case #%d
",++ti);
- printf("Total explored area: %.2lf
",ans);
-
- }
- return 0;
- }
また、矩形分割も矩形領域に関する問題を解くことができます.