题目链接:
https://www.luogu.com.cn/problem/P1330
参考博客:
https://www.luogu.com.cn/blog/Kesdiael3/solution-p1330
这个博客解释的很详细,建议复习的时候可以再看看
思路:
1:肯定要明确一点,那就是这个图是不一定联通的。于是,我们就可以将整张图切分成许多分开的联通子图来处理。然而最重要的事情是:如何处理一个连通图?
2:每一条边都有且仅有一个被它所连接的点被选中:对于这一个图的点的选法,可以考虑到相邻的点染成不同的颜色
3:于是,对于一个连通图,要不就只有两种选法,要不就是impossible!
4:所以,我们只需要找到每一个子连通图,对它进行黑白染色,然后取两种染色中的最小值,然后最后汇总,就可以了
5:另外,要判断impossible,只需要加一个used数组,记录已经遍历了哪些点。如果重复遍历一个点,且与上一次的颜色不同,则必然是impossible的
#include <bits/stdc++.h>
using namespace std;
int n,m,a,b,cnt,head[20002],sum[2];//黑白两种染色各自的点数
bool used[20002]={0};//是否遍历过
int col[20002]={0};//每一个点的染色
struct Edge
{
int t;
int next;
}edge[200002];
//链式前向星存边
void add(int a,int b)
{
cnt++;
edge[cnt].t=b;
edge[cnt].next=head[a];
head[a]=cnt;
}
//染色(返回false即impossible)
bool dfs(int x,int color)
{
if(used[x])//如果已被染过色
{
if(col[x]==color)return true;//如果仍是原来的颜色,即可行
return false;//非原来的颜色,即产生了冲突,不可行
}
used[x]=true;//记录
sum[col[x]=color]++;//这一种颜色的个数加1,且此点的颜色也记录下来
bool tf=true;//是否可行
for(int i=head[x];i!=0&&tf;i=edge[i].next)//遍历边
{
tf=tf&&dfs(edge[i].t,1-color);//是否可以继续染色
}
return tf;//返回是否完成染色
}
int main()
{
ios::sync_with_stdio(0);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
add(a,b),add(b,a);
}
int ans=0;
for(int i=1;i<=n;i++)
{
if(used[i])continue;//如果此点已被包含为一个已经被遍历过的子图,则不需重复遍
sum[0]=sum[1]=0;//初始化
if(!dfs(i,0))//如果不能染色
{
printf("Impossible");
return 0;//直接跳出
}
ans+=min(sum[0],sum[1]);//加上小的一个
}
printf("%d",ans);
return 0;
}