poj 1838
这道题主要是对并查集的考察,在这道题的解题过程中主要用到的算法就是并查集中的最基本的makeSet,findSet,unionSet
即前篇文章中所提到的:
makeSet(Elem x);主要实现的是,将传入的数据元素初始化为一个集合。
findSet(Elem x); 这个并查集中的原子算法主要实现的是查找到元素x所在的集合,
而这个集合主要使用集合中的祖先元素所唯一标识的。
unionSet(Elem x, Elem y);这个方法主要实现的是合并元素x,元素y所在的集合。
POJ 1838 解题报告
题目具体描述的什么意思就不翻译了,
输入样例中有N,K两个数值。
大致的意思是说,有N*N这么多个区域,区域上有相应的点(x,y)
当两个点point(x1,y1),(x2,y2) 之间是相邻的(neighbor)那么这两个点必须满足下面的条件之一:
1. x1==x2&&|y1-y2|==1
2. y2==y1&&|x1-x2|==1
根据相邻这一判定条件,可以把整个N*N个区域划分成m个区间(region)
即,region中的点是依靠两两之间作为neighbor来连接的。
现如今,需要你输出K个区间(region),并且要求这K个区间对应的区间中的点数之和达到最大。
(也就是说从m个区间中选取K个区间出来,是的这K个区间中的点数之和达到最大)
将m对应的数据结构排序(大到小),然后选取前K个,进行加和,然后进行输出,即可。
大致的算法流程如下:
readIn(N,K)
for (i 0-> N-1)
{readIn(trees[i].x , trees[i].y)
trees[i].key = i
}
makeSet(trees[i].key);
//finish initialization
qsort(trees.x);
for(i 0->N-2)
{
if(trees[i+1].x==trees[i].x && trees[i+1].y- trees[i].y==1)
unionSet(trees[i+1].key, trees[i].key);
}
qsort(trees.y);
for(i 0 ->N-2)
{
if(trees[i+1].y==trees[i].y && trees[i+1].x- trees[i].x==1)
unionSet(trees[i+1].key, trees[i].key);
}
j=0;
for(i 0-> N-1)
{
if(trees[i].key 是祖先节点)
setList[j++] = setNumber[trees[i].key];
}
after this loop:
the value of j is the length of setList
qsort(setList, j, sizeof(int ), compare_method);
after this
get setList[0 .. k-1] value and add
printf the sum
//union-find set
#include<stdio.h>
#include<algorithm>
typedef struct
{
int x;
int y;
int key;
}Tree;
//这个数据结构是根据问题而构建的
Tree trees[16001]; //这个数组用来存放节点的
int father[16001]; //father[i]用来表示的是trees[i]这个元素的父节点在trees中的位序下标
int rank[16001]; //这个是用来记录每个集合对应的树模型的树高,秩在并查集中通常以树高表示的,但也有以树状集合中的元素总数来代替的
int setNumber[16001]; //setNumber[i] 对应的是地i个元素的子元素的个数,如果i是它所在集合的祖先节点的话,setNumber[i]
int setList[16001]; //用于记录祖先节点所在的集合对应的集合中的总共的元素的个数的
int cmp(const void *a, const void *b)
{
return *(int *)b - *(int *)a;
}
//这个函数用于根据集合中元素的数目从大到小对setList数组进行排序
//对,因为是大到小排序,所以在返回值上面是参数2-参数1,
//如果是从小到大进行排序,则是参数1-参数2
int cmp_x(const void *a, const void *b)
{
Tree *t1, *t2;
t1=(Tree*)a;
t2=(Tree*)b;
if(t1->x!= t2->x)
return t1->x-t2->x;
else
return t1->y-t2->y;
}
//在对trees[i].x进行小到大排序的时候,如果trees[i].x的值相等,则排序的顺序将会根据trees[i].y的值从小到大进行相应的排序。
//又因为题中给出每个坐标上只有一棵香蕉树,所以测试数据不会有重复输入两次相同坐标的情况。
int cmp_y(const void *a, const void *b)
{
Tree *t1, *t2;
t1 = (Tree*)a;
t2 = (Tree*)b;
if(t1->y != t2->y)
return t1->y - t2->y;
else
return t1->x - t2->x;
}
void makeSet(int x)
{
father[x] = x;
rank[x] = 0;
setNumber[x]=1;
}
int findSet(int x)
{
if(father[x]!=x)
father[x] = findSet(father[x]);
return father[x];
}
void unionSet(int x, int y)
{
int rootx = findSet(x);
int rooty = findSet(y);
if(rootx == rooty) return ;
if(rank[rootx] < rank[rooty])
{
father[rootx] = rooty;
setNumber[rooty] += setNumber[rootx];
}
else
{
if(rank[rootx] == rank[rooty])
{
rank[rootx]++;
}
father[rooty]=rootx;
setNumber[rootx]+= setNumber[rooty];
}
}
int main()
{
int n, k, j, i, ans;
scanf("%d %d", &n, &k);
for(i = 0 ; i < n ; i++)
{
scanf("%d %d", &trees[i].x, &trees[i].y);
trees[i].key = i;
makeSet(trees[i].key);
}
qsort(trees, n, sizeof(Tree), cmp_x);
for(i = 0 ; i < n-1; i++)
{
if(trees[i].x == trees[i+1].x && trees[i+1].y - trees[i].y ==1)
{
unionSet(trees[i].key, trees[i+1].key);
}
// printf("x:%d y:%d key:%d
", trees[i].x, trees[i].y, trees[i].key);
}
qsort(trees, n, sizeof(Tree),cmp_y);
for(i = 0 ; i < n-1; i++)
{
if(trees[i].y == trees[i+1].y && trees[i+1].x-trees[i].x ==1)
{
unionSet(trees[i].key, trees[i+1].key);
}
// printf("y:%d x: %d key:%d
", trees[i].y, trees[i].x, trees[i].key);
}
j = 0 ;
for(i = 0 ; i < n ; i++)
{
if(father[trees[i].key]==trees[i].key)
{
setList[j++] = setNumber[trees[i].key];
}
}
qsort(setList, j, sizeof(int), cmp);
//printf("region number:%d
", j);
ans = 0 ;
for(i = 0 ; i <k; i++)
{
ans+=setList[i];
//printf("setList[i] %d
", setList[i]);
}
printf("%d
", ans);
return 0;
}
memory/time/language/code length/date
604K | 172MS | C++ | 2197B | 2013-07-24 10:53:57 |
//考虑到这个rank也就是代表每个节点的秩,祖先元素的秩也就是树的高度,在本题中没有太多的实际用途,
//这是因为,在进行unionSet方法的时候,单纯地对元素个数进行比较,
//然后将少的元素所对应的集合的祖先节点所谓到元素多的集合的祖先节点的直系孩子
//也是可以实现相同的结果的,于是把rank变量从代码中进行删除
//然后稍稍更改一下unionSet(int x, int y )这个方法,代码如下:
void unionSet(int x, int y) { int rootx = findSet(x); int rooty = findSet(y);
if(rootx == rooty) return ;
if(setNumber[rootx]< setNumber[rooty])
{
father[rootx] = rooty;
setNumber[rooty] += setNumber[rootx];
}
else
{
father[rooty]=rootx;
setNumber[rootx]+= setNumber[rooty];
}
}
memory/time/language/code length/date
540K | 172MS | C++ | 2264B | 2013-07-24 11:28:48 |