题目链接:
https://www.luogu.com.cn/problem/P1330
参考博客:
https://www.luogu.com.cn/blog/D-fzy/feng-suo-yang-guang-tai-xue
这道题目自己写了三篇题解,分别是不同的思路,这个题解参考上边博客的代码,但是它讲的并不清楚,于是我解释如下,也是大致理解
算法:
并查集
代码解释:
1:f[]数组存根节点,t[]数组存以某个节点为根节点的树(联通分量的)所包含的节点个数,bj[]用于最后标记某节点是否已经被访问过,h[i]表示与i节点不同颜色的(不在同一棵树中的)节点
for(int i=1;i<=m;i++)
{
cin>>a>>b;
int x1=find(a),x2=find(b);
if(x1!=x2)
{
if(h[a])xx(h[a],x2);
if(h[b])xx(h[b],x1);
h[a]=x2;
h[b]=x1;
}
else
{
cout<<"Impossible"<<endl;
return 0;
}
}
2:注意在同一棵树中(同一个强连通分量)中的节点颜色是相同的
3:
cin>>a>>b;
int x1=find(a),x2=find(b);
if(x1!=x2)
a,b是同一条边的两个节点,那么这两个节点的颜色就不能相同,即所属的树不同,即树根不同,(即处于不同的强联通分量)
4:那么与a异色的点与b同色,即,与a异色的点h[a]与b的树根x2,同色,在同一棵数中,对b的分析同理
5:h数组存a点的异色点,对b的分析同理
if(x1!=x2)
{
if(h[a])xx(h[a],x2);
if(h[b])xx(h[b],x1);
h[a]=x2;
h[b]=x1;
}
6:如果他们同色,表示不行(即a和b同根,在同一棵树中,颜色相同)
else
{
cout<<"Impossible"<<endl;
return 0;
}
7:bj[]用于最后标记某节点是否已经被访问过
8:肯定要明确一点,那就是这个图是不一定联通的,因此:
for(int i=1;i<=n;i++)
{
int q=find(i);
if(!bj[q])
{
int q1=find(h[i]);
bj[q]=1;
bj[q1]=1;
ans+=min(t[q],t[q1]);
}
}
#include <bits/stdc++.h>
using namespace std;
int f[10001],a,b,n,m,t[10001],bj[10001],h[10001],ans;
void init()
{
for(int i=1;i<=n;i++)
{
f[i]=i;
t[i]=1;
}
}
int find(int x)
{
if(f[x]!=x) f[x]=find(f[x]);
return f[x];
}
void xx(int x,int y)
{
int qq=find(x);
int qq1=find(y);
if(qq!=qq1)
{
f[qq1]=qq;
t[qq]+=t[qq1];
}
}
int main()
{
ios::sync_with_stdio(0);
cin>>n>>m;
init();
for(int i=1;i<=m;i++)
{
cin>>a>>b;
int x1=find(a),x2=find(b);
if(x1!=x2)
{
if(h[a])xx(h[a],x2);
if(h[b])xx(h[b],x1);
h[a]=x2;
h[b]=x1;
}
else
{
cout<<"Impossible"<<endl;
return 0;
}
}
for(int i=1;i<=n;i++)
{
int q=find(i);
if(!bj[q])
{
int q1=find(h[i]);
bj[q]=1;
bj[q1]=1;
ans+=min(t[q],t[q1]);
}
}
cout <<ans<< endl;
return 0;
}