POJ 1838 Banana(并查集)

38 篇文章 0 订阅

Description
一个点用坐标(x,y)表示,如果两个点在水平方向或垂直方向上相邻,则两个点属于一个区域,即点1(x1,y1),点2(x2,y2)相邻当且仅当x1==x2,|y1-y2|=1或者|x1-x2|=1,y1=y2。给定一些点,相邻的点构成一个区域,求出k个区域所能拥有的最大点数(k不大于区域数)
Input
第一行两个整数n和k分别代表点数和区域数,之后n行为n个点的坐标
Output
输出k个区域所能拥有的最大点数
Sample Input
10 3
7 10
1 1
101 1
2 2
102 1
7 11
200 202
2 1
3 2
103 1
Sample Output
9
Solution
可用并查集解决,一个区域表示为一个并查集,两个区域相邻时,合并这两个并查集。同时记录并查集中的点数目。
初始时,每个点为一个并查集。对x坐标相同、y坐标相差为1的点,合并它们所在的并查集,合并时需要判断两个点是否位于同一个集合。合并时需要先找到各自集合的根节点,然后让其中一个根节点指向另一个根节点完成合并。对y坐标相同、x坐标相差为1的点,合并它们所在的并查集。
当所有相邻点进行了合并操作之后,对并查集点数目从大到小排序,取前K个值的总和作为结果输出。
Code

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define INF (1<<29)
#define maxn 16005
struct node
{
    int x,y,flag;//x,y记录点坐标,flag记录点的下标便于操作 
}pos[maxn];
int par[maxn];
int rank[maxn];
int mem[maxn];
int ans[maxn];
int n,k,num;
void init(int n)
{
    for(int i=0;i<n;i++)
    {
        par[i]=i;
        rank[i]=0;
        mem[i]=1;
        pos[i].flag=i;
    }
}
int find(int x)
{
    if(par[x]==x)
        return x;
    return par[x]=find(par[x]);
}
void unite(int x,int y)
{
    x=find(x);
    y=find(y);
    if(x==y)
        return ;
    if(rank[x]<rank[y])
    {
        par[x]=y;
        mem[y]+=mem[x];//合并区域点数相加 
    }
    else
    {
        par[y]=x;
        mem[x]+=mem[y];//合并区域点数相加 
        if(rank[x]==rank[y])
            rank[x]++;
    }
}
int cmp_x(node a,node b)
{
    if(a.x==b.x)
        return a.y<b.y;
    return a.x<b.x;
}
int cmp_y(node a,node b)
{
    if(a.y==b.y)
        return a.x<b.x;
    return a.y<b.y;
}
int cmp(int x,int y)
{
    return x>y;
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++)
        scanf("%d%d",&pos[i].x,&pos[i].y);
    init(n);
    sort(pos,pos+n,cmp_x);//对点按横坐标升序排 
    for(int i=0;i<n-1;i++)//对x坐标相同、y坐标相差为1的点,合并它们所在的并查集
        if(pos[i].x==pos[i+1].x&&pos[i+1].y-pos[i].y==1)
            unite(pos[i].flag,pos[i+1].flag);
    sort(pos,pos+n,cmp_y);//对点按纵坐标升序排 
    for(int i=0;i<n-1;i++)//对y坐标相同、x坐标相差为1的点,合并它们所在的并查集 
        if(pos[i].y==pos[i+1].y&&pos[i+1].x-pos[i].x==1)
            unite(pos[i].flag,pos[i+1].flag);
    int res=0;
    for(int i=0;i<n;i++)//找到每个区域的根节点,记录该区域的点数 
        if(par[i]==i)
            ans[res++]=mem[i];
    sort(ans,ans+res,cmp);//对每个区域点数按降序排 
    for(int i=0;i<k;i++)//取前k个区域即为最大值 
        num+=ans[i];
    printf("%d\n",num);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值