洛谷 P1330 封锁阳光大学

题目大意:

有一个图,图中有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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值