本题的做法是万万没有想到的,我觉得三个人都没想到这方面确实是解决问题的能力亟待加强
可以将每对数都连上一条边,这样本题就变成了图论的问题,那么这个图肯定是若干个连通块,对于一个含有 个顶点的连通块,如果恰好有 条边,那么只能选择 个点,否则一定能选择 个点,那么考虑使用并查集解决这个问题,实际上就是判断每个集合中是否含有环
实际上本题并不是严格的并查集判断是否含有环,因为一条边如果出现了两次,那么该边连接的两个点都可以选,进而这个集合就能选出所有的点。参考了其他博客的做法,只需使用一个数组记录每个集合是否出现加入的边的端点属于这一集合,然后如果将一个集合合并到另外一个集合时,如果原来的两个祖先有一个被标记,那么只需标记到新的祖先。最后先设答案为所有的点,如果有集合不存在环那么减一,最后得到的就是答案
#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define ins insert
#define Vector Point
#define lowbit(x) (x&(-x))
#define mkp(x,y) make_pair(x,y)
#define mem(a,x) memset(a,x,sizeof a);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<double,double> pdd;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const double dinf=1e300;
const ll INF=1e18;
const int Mod=1e9+7;
const int maxn=2e5+10;
int f[maxn],cnt[maxn];
bool vis[maxn];
unordered_map<int,int> mp,pnum;
int Find(int x){
return f[x]==x?x:f[x]=Find(f[x]);
}
int main(){
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
//ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t,n,kase=0;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
int num=0;
mp.clear(),pnum.clear();
for(int i=1;i<=2*n;i++) f[i]=i,vis[i]=0;
for(int i=1,x,y;i<=n;i++){
scanf("%d %d",&x,&y);
if(!mp.count(x)) mp[x]=++num;
if(!mp.count(y)) mp[y]=++num;
int fx=Find(mp[x]),fy=Find(mp[y]);
if(fx!=fy){
f[fy]=fx;
if(vis[fx] || vis[fy]) vis[fx]=1,vis[fy]=0;
}else vis[fx]=1;
}
int ans=num;
for(int i=1;i<=num;i++) if(f[i]==i && !vis[i]) ans--;
printf("Case #%d: %d\n",++kase,ans);
}
return 0;
}