题目链接: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; }