题目大意:
有一个图,图中有n个点,m条边,我们可以对其中的一些点打标志,被打标志的点会影响邻接所有的边,问为了把所有边都影响,同时最少的打标志的点的点数。同时相邻的点不能打标志。
解题思路:
一开始以为是贪心,利用最小生成树的思路去做,每次选择度数最大的点,这种解法是错的!
其实这题考了偶图,我们只需要按照偶图来进行染色,一个连通分量中我们可以从任意一个点开始染色,然后黑白黑白交叉染,选择黑白染色数少的那个ans1,第二次再染不过起点颜色换另外一个得到ans2,最后min(ans1,ans2)得到本个连通分量的染色数,最后所有的连通分量的染色数加起来即可。
什么时候输出Impossible呢?根据偶图的性质,有奇圈则不是偶图,要输出Impossible
#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e4+10;
int dfs_parent[MAXN];
int dfs_num[MAXN];
int nxa[MAXN];
const int UNVISITED=0;
const int VISITED=1;
const int EXPLORED=2;
vector<vector<int>> gra(MAXN);
void dfs(int u,int level){
dfs_num[u]=VISITED;
nxa[u]=level;
for(int i=0;i<(int)gra[u].size();i++){
int nx=gra[u][i];
if(dfs_num[nx]==UNVISITED){
dfs(nx,level+1);
dfs_parent[nx]=u;
}else if(dfs_num[nx]==VISITED){
if(dfs_parent[nx]==u)continue;
if((nxa[u]-nxa[nx])%2==0){
cout<<"Impossible"<<endl;
exit(0);
}
}else if(dfs_num[nx]==EXPLORED){
continue;
}
}
dfs_num[u]=EXPLORED;
}
int res[2];
int dfs_flag[MAXN];
void dfs2(int u,int color){
//cerrr<<u<<endl;
dfs_num[u]=VISITED;
dfs_flag[u]=VISITED;
res[color]++;
for(int i=0;i<(int)gra[u].size();i++){
int nx=gra[u][i];
if(dfs_flag[nx]==VISITED)continue;
dfs2(nx,!color);
}
}
int main(){
int n,m;cin>>n>>m;
set<pair<int,int>> st;
for(int i=0;i<m;i++){
int a,b;cin>>a>>b;
a-=1;
b-=1;
if(st.count(make_pair(a,b)))continue;
else
{st.insert(make_pair(a,b));
st.insert(make_pair(b,a));
}
gra[a].emplace_back(b);
gra[b].emplace_back(a);
}
memset(dfs_num,UNVISITED,sizeof(dfs_num));
for(int i=0;i<n;i++){
if(dfs_num[i]==UNVISITED){
dfs(i,0);
}
}
memset(dfs_num,UNVISITED,sizeof(dfs_num));
memset(dfs_flag,UNVISITED,sizeof(dfs_flag));
int ans=0;
for(int i=0;i<n;i++){
if(gra[i].size()==0)continue;
memset(res,0,sizeof(res));
if(dfs_num[i]==UNVISITED){
dfs2(i,0);
int ans1=min(res[0],res[1]);
memset(res,0,sizeof(res));
memset(dfs_flag,UNVISITED,sizeof(dfs_flag));
dfs2(i,1);
int ans2=min(res[0],res[1]);
ans+=min(ans1,ans2);
}
}
cout<<ans<<endl;
return 0;
}