HDU 1558 Segment set( 判断线段相交 + 并查集 )


链接:传送门

题意:输入一个数 n 代表有 n 组操作,P 是在平面内加入一条线段,Q x 是查询第 x 条线段所在相交集合的线段个数

  • 例如:下图 5 与 1、2 相交,1 与 3 相交,2 与 4 相交,所以这个相交集合的线段为 1、2、3、4、5,所以 Q 5 答案为 5
    o_%e7%81%ab%e7%8b%90%e6%88%aa%e5%9b%be_2017-05-08T06-14-22.598Z.png

思路:

  1. 可以使用并查积来描述“相交集合”,如果两个线段相交,就 unite 这两个线段所在集合,需要注意的是,在进行 unite 的时候,需要对两个集合中所有元素进行处理

  2. 如何查询每个集合中线段的个数?题目给的数据量较小,可以直接扫描整个 par[] 数组来记录个数

balabala:下面两种做法再次体现了知识面的决定性作用TAT!方法1——46 Ms AC ,方法2——156 Ms AC!接近4倍的时间!


方法1:加一个数组维护集合中线段的个数,这个想法来源于这一道题 ---> 戳这里!

/*************************************************************************
    > File Name: hdu1558t2.cpp
    > Author:    WArobot 
    > Blog:      http://www.cnblogs.com/WArobot/ 
    > Created Time: 2017年05月08日 星期一 19时38分36秒
 ************************************************************************/

#include<bits/stdc++.h>
using namespace std;

#define eps 1e-10
const int maxn = 1010;
struct point{ double x,y; };
struct V{ point s,e; };
int par[maxn] , num[maxn];  

// 线段相交部分
bool inter(point a,point b,point c,point d){
    if( min(a.x,b.x) > max(c.x,d.x) ||
    min(a.y,b.y) > max(c.y,d.y) ||
    min(c.x,d.x) > max(a.x,b.x) ||
    min(c.y,d.y) > max(a.y,b.y)
    )return 0;
    double h,i,j,k;
    h = (b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x);
    i = (b.x-a.x)*(d.y-a.y) - (b.y-a.y)*(d.x-a.x);
    j = (d.x-c.x)*(a.y-c.y) - (d.y-c.y)*(a.x-c.x);
    k = (d.x-c.x)*(b.y-c.y) - (d.y-c.y)*(b.x-c.x);
    return h*i<=eps && j*k<=eps;
}
// 并查集部分
void init(){
    for(int i=0;i<maxn;i++){
        par[i] = i;
        num[i] = 1;
    }
}
int find(int x){
    if(par[x] == x) return x;
    else            return par[x] = find(par[x]);
}
void unite(int x,int y){
    x = find(x);
    y = find(y);
    if(x==y)    return;
    else{
        par[y] = x;
        num[x] += num[y];   // 在合并的同时维护集合数目
    }
}

void solve(int k,int x){
    int fx = find(x);
    unite( k , x );
    /*for(int i = 0 ; i < k ; i++){
        if( par[i] == fx )  par[i] = k;
    }*/
}
int main(){
    int T , N , x , k , kase = 0;;
    string op;
    V v[maxn];
    scanf("%d",&T);
    while(T--){
        if( kase > 0 )  printf("\n");
        kase++;

        init();     // 初始化并查集
    
        scanf("%d",&N);
        k = 0;
        while(N--){
            cin>>op;
            if( op == "P" ){
                scanf("%lf%lf%lf%lf", &v[k].s.x , &v[k].s.y , &v[k].e.x , &v[k].e.y );   
                for(int i = 0 ; i < k ; i++){
                    if( inter(v[k].s,v[k].e,v[i].s,v[i].e) )
                        solve( k , i );
                }
                k++;
            }
            else{
                scanf("%d",&x);     x -= 1;
                /*int cnt = 0;
                for(int i = 0 ; i <= k ; i++){
                    if( par[i] == par[x] ) cnt++;
                }
                printf("%d\n",cnt);*/
                printf("%d\n", num[ find(x) ] );
            }
        }
        /*for(int i = 0 ; i < k ; i++){
            printf("i = %d , par[i] = %d\n",i,par[i]);
        }*/
    }
    return 0;
}

方法2:非常非常蠢的写法

/*************************************************************************
    > File Name: hdu1558.cpp
    > Author:    WArobot 
    > Blog:      http://www.cnblogs.com/WArobot/ 
    > Created Time: 2017年05月08日 星期一 00时00分36秒
 ************************************************************************/

#include<bits/stdc++.h>
using namespace std;

#define eps 1e-10
const int maxn = 1010;
struct point{ double x,y; };
struct V{ point s,e; };
int par[maxn];  

// 线段相交部分
bool inter(point a,point b,point c,point d){
    if( min(a.x,b.x) > max(c.x,d.x) ||
    min(a.y,b.y) > max(c.y,d.y) ||
    min(c.x,d.x) > max(a.x,b.x) ||
    min(c.y,d.y) > max(a.y,b.y)
    )return 0;
    double h,i,j,k;
    h = (b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x);
    i = (b.x-a.x)*(d.y-a.y) - (b.y-a.y)*(d.x-a.x);
    j = (d.x-c.x)*(a.y-c.y) - (d.y-c.y)*(a.x-c.x);
    k = (d.x-c.x)*(b.y-c.y) - (d.y-c.y)*(b.x-c.x);
    return h*i<=eps && j*k<=eps;
}
// 并查集部分
void init(){
    for(int i=0;i<maxn;i++){
        par[i] = i;
    }
}
int find(int x){
    if(par[x] == x) return x;
    else            return par[x] = find(par[x]);
}
void unite(int x,int y){
    x = find(x);
    y = find(y);
    if(x==y)    return;
    else        par[y] = x;
}

void solve(int k,int x){
    int fx = find(x);   // 先找到 x 的祖先再合并 x,否则合并之后就无法处理原本 x 所在集合的所有元素了
    unite( k , x );
    for(int i = 0 ; i < k ; i++){
        if( par[i] == fx )  par[i] = k;
    }
}
int main(){
    int T , N , x , k , kase = 0;;
    string op;
    V v[maxn];
    scanf("%d",&T);
    while(T--){
        if( kase > 0 )  printf("\n");
        kase++;

        init();     // 初始化并查集
    
        scanf("%d",&N);
        k = 0;
        while(N--){
            cin>>op;
            if( op == "P" ){
                scanf("%lf%lf%lf%lf", &v[k].s.x , &v[k].s.y , &v[k].e.x , &v[k].e.y );   
                for(int i = 0 ; i < k ; i++){
                    if( inter(v[k].s,v[k].e,v[i].s,v[i].e) )
                        solve( k , i );
                }
                k++;
            }
            else{
                scanf("%d",&x);     x -= 1;
                int cnt = 0;
                for(int i = 0 ; i <= k ; i++){
                    if( par[i] == par[x] ) cnt++;
                }
                printf("%d\n",cnt);
            }
        }
        /*for(int i = 0 ; i < k ; i++){
            printf("i = %d , par[i] = %d\n",i,par[i]);
        }*/
    }
    return 0;
}

转载于:https://www.cnblogs.com/WArobot/p/6824883.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
社会的进步导致人们对于学习的追求永不止境,那么追求农业信息化的方式也从单一的田地教程变成了多样化的学习方式。多样化的学习方式不仅仅是需要人们智慧的依靠,还需要能够通过软件的加持进行信息化的价值体现。软件和系统的产生,从表面上来看是方便了某一行业和某一行业的用户,其实是从本质上来说是提高了社会的进步。就拿我们常见的出行而言,滴滴出行看似是为了打车和出行的人方便,但其实通过另外一种程度上来说,可以通过软件应用的调度和发展来为社会、城市出行的发展做出巨大的贡献。我们国家从最早的中国制造业演变到现在的“智造”,就是因为有软件信息系统的价值,能够将一些智慧的因素加入到制造的过程当中,而这一点就是软件系统来改变生产和现实的需求。在计算机时代日益发展的今天,计算机网络正快速融入这个社会的每一个领域。农业的发展是社会当中一种必有可少的方式。果树在种植和培养是直接影响果农及果商的发展,但在果树的资源管理方面还是有着很大的不同,所以信息多样化的果树管理方式很重要。在传统的果树资源管理上还有着很大的约束,为此开发和设计JSP杏种质资源管理系统,该系统内容丰富多彩,用户可以在线进行果杏树的资源查询等。本文还是使用JSP的方式来进行管理的,但在系统建设过程当中也考虑了许许多多信息安全的保护。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值