题目:
一个点用坐标(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;
}