题目:
一个点用坐标(x,y)表示,如果两个点在水平方向或垂直方向上相邻,则两个点属于一个区域,即点1(x1,y1),点2(x2,y2)相邻当且仅当x1==x2,|y1-y2|=1或者|x1-x2|=1,y1=y2。给定一些点,相邻的点构成一个区域,求出k个区域所能拥有的最大点数。(k不大于区域数)。
解题思路:
可用并查集解决,一个区域表示为一个并查集,两个区域相邻时,合并这两个并查集。同时记录并查集中的点数目。
初始时,每个点为一个并查集。
对x坐标相同、y坐标相差为1的点,合并它们所在的并查集,合并时需要判断两个点是否位于同一个集合。合并时需要先找到各自集合的根节点,然后让其中一个根节点指向另一个根节点完成合并。
对y坐标相同、x坐标相差为1的点,合并它们所在的并查集。
当所有相邻点进行了合并操作之后,对并查集点数目从大到小排序,取前K个值的总和作为结果输出。
代码:
#include<stdio.h>
//定义点的数据结构
typedef struct
{
int x,y,index;
} Point;
//定义全局变量
Point data[16000];
int father[16000];
int rank[16000];
int mem[16000];
int result[16000];
//定义排序函数
int cmp(const void *a, const void *b)
{
return *(int*)b-*(int*)a;
}
int cmp_x(const void *a, const void *b)
{
Point *p1,*p2;
p1=(Point*)a;
p2=(Point*)b;
if(p1->x != p2->x) return p1->x - p2->x;
else return p1->y - p2->y;
}
int cmp_y(const void *a, const void *b)
{
Point *p1,*p2;
p1=(Point*)a;
p2=(Point*)b;
if(p1->y != p2->y) return p1->y - p2->y;
else return p1->x - p2->x;
}
/*定义并查集的三种操作
* make_set(x)
* find_set(x)
* union_set(x,y)
*/
void make_set(int x)
{
father[x]=x; //父节点设为自身,表示根节点
rank[x]=0; //秩设为0
mem[x]=1; //集合节点数目为1
}
//查找x所在集合的根节点
int find_set(int x)
{
if(father[x] != x)
father[x]=find_set(father[x]); //递归查找,路径压缩
return father[x];
}
//合并两个集合
void union_set(int x, int y)
{
int rootx=find_set(x);
int rooty=find_set(y);
if(rootx == rooty) return; //两节点位于同一个集合
if(rank[rootx] < rank[rooty]) //y所在树高,挂到y树上
{
father[rootx]=rooty;
mem[rooty] += mem[rootx];
}
else //挂到x树上
{
if(rank[rootx]==rank[rooty]) rank[rootx]++;
father[rooty]=rootx;
mem[rootx] += mem[rooty];
}
}
int main()
{
int n,k,i,j,ans;
//输入n,k
scanf("%d %d", &n, &k);
for(i=0; i<n; i++)
{
scanf("%d %d", &data[i].x, &data[i].y);
data[i].index = i;
make_set(i);
}
//按x坐标排序
qsort(data, n, sizeof(Point), cmp_x);
for(i=0; i<n-1; i++)
{
if(data[i].x == data[i+1].x && data[i+1].y-data[i].y==1)
union_set(data[i].index, data[i+1].index);
}
//按y坐标排序
qsort(data, n, sizeof(Point), cmp_y);
for(i=0; i<n-1; i++)
{
if(data[i].y==data[i+1].y && data[i+1].x-data[i].x==1)
union_set(data[i].index, data[i+1].index);//注意不是 union_set(i,i+1)
}
//赋值region的点数目
j=0;
for(i=0; i<n; i++)
{
if(father[i]==i) result[j++]=mem[i]; //根节点的mem值代表了该集合的点数目
}
//对result数组按从大到小排序
qsort(result, j, sizeof(int), cmp);
//结果为result数组前k个元素的总和
ans=0;
for(i=0; i<k; i++) ans+=result[i];
//输出结果
printf("%d\n", ans);
return 0;
}
本文介绍使用并查集算法解决求多个点构成的最大相邻区域数量问题,通过合并水平或垂直相邻的点来形成区域,并找出特定数量下最大的区域点数。
139

被折叠的 条评论
为什么被折叠?



