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