转载请注明出处:忆梦http://blog.csdn.net/fjy4328286/article/details/9534955
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4619
题目大意:输入n m, n代表横着的多米诺骨牌个数, m代表竖着的多米诺骨牌个数。
输入n个点,x y 代表(x,y)和(x+1,y)处为横着的多米诺骨牌覆盖的点
输入m个点,x y 代表(x,y)和(x,y+1)处为竖着的多米诺骨牌覆盖的点
题目中还有一个重要的条件,只有横着的多米诺骨牌和竖着的多米诺骨牌还可能互相覆盖,所以一个点最多被覆盖两次(横着竖着分别一次)。
题解:比赛的时候就想用并查积来写,但是不知道思路会不会有错,就没有敲。 赛后试了一下,果然A了。思路很简单,将每一条链上的点归为一堆(用并查集来实现),将链上的点数统计一下个数,然后再除以2就是这条链应该有的边数。将矩阵中所有链按上述方法来算边数,最后求边数和就行了。
#include<cstdio>
#include<iostream>
#include<vector>
using namespace std;
#define N 2050
#define MAXN 105
int F[N];
int fun(int x)
{
if(F[x] != x)
F[x] = fun(F[x]);
return F[x];
}
int main ()
{
int n, m;
while(scanf("%d %d", &n, &m), (n||m))
{
int i, j, k;
for(i = 1; i <= n+m; i++)
F[i] = i;
vector<int>s[MAXN][MAXN];
int a,b;
for(i = 1; i <= n; i++)
{
scanf("%d %d", &a, &b);
s[a][b].push_back(i);
s[a+1][b].push_back(i);
}
for(i = n + 1; i <= n+m; i++)
{
scanf("%d %d", &a, &b);
s[a][b].push_back(i);
s[a][b+1].push_back(i);
}
for(i = 0; i < MAXN; i++)
for(j = 0; j < MAXN; j++)
{
if(s[i][j].size() == 2)
{
a = s[i][j][0];
b = s[i][j][1];
a = fun(a);
b = fun(b);
if(a != b)
F[a] = b;
}
}
int root[N], it = 0;
int cnt[N];
memset(cnt, 0 ,sizeof(cnt));
for(i = 0; i < MAXN; i++)
for(j = 0; j < MAXN; j++)
{
if(s[i][j].size() != 0)
{
int temp = fun(s[i][j][0]);
for(k = 0; k <= it; k++)
if(temp == root[k])
break;
if(k > it)
{ cnt[it]++;root[it++] = temp;}
else
cnt[k]++;
}
}
int ans = 0;
for(i = 0; i < N; i++)
ans += cnt[i]/2;
printf("%d\n", ans);
}
return 0;
}