此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置。
题目链接:https://www.luogu.org/problemnew/show/P1330
题目描述
曹是一只爱刷街的老曹,暑假期间,他每天都欢快地在阳光大学的校园里刷街。河蟹看到欢快的曹,感到不爽。河蟹决定封锁阳光大学,不让曹刷街。
阳光大学的校园是一张由N个点构成的无向图,N个点之间由M条道路连接。每只河蟹可以对一个点进行封锁,当某个点被封锁后,与这个点相连的道路就被封锁了,曹就无法在与这些道路上刷街了。非常悲剧的一点是,河蟹是一种不和谐的生物,当两只河蟹封锁了相邻的两个点时,他们会发生冲突。
询问:最少需要多少只河蟹,可以封锁所有道路并且不发生冲突。
输入输出格式
输入格式:
第一行:两个整数N,M
接下来M行:每行两个整数A,B,表示点A到点B之间有道路相连。
输出格式:
仅一行:如果河蟹无法封锁所有道路,则输出“Impossible”,否则输出一个整数,表示最少需要多少只河蟹。
输入输出样例
3 3 1 2 1 3 2 3
Impossible
3 2 1 2 2 3
说明
【数据规模】
1<=N<=10000,1<=M<=100000,任意两点之间最多有一条道路。
分析
这一题是一个好题。如果知道思路了,便会非常简单。但是如果不知道思路,却比较的难想出来。
我们来分析一下题目。首先,肯定要明确一点,那就是这个图是不一定联通的。于是,我们就可以将整张图切分成许多分开的连同子图来处理。然而最重要的事情是:如何处理一个连通图?
乍看下去,似乎无从下手,因为方案好像有很多种,根本就枚举不完。但是,关键要注意到题目中重要的两个条件,我们把它抽象成这两个要素:
①每一条边所连接的点中,至少要有一个被选中。②每一条边所连接的两个点,不能被同时选中。由此,可以推断出:
每一条边都有且仅有一个被它所连接的点被选中。
又因为我们要处理的是一个连通图。所以,对于这一个图的点的选法,可以考虑到相邻的点染成不同的颜色。
于是,对于一个连通图,要不就只有两种选法(因为可以全部选染成一种色的,也可以全部选染成另一种色的),要不就是impossible!
所以,我们只需要找到每一个子连通图,对它进行黑白染色,然后取两种染色中的最小值,然后最后汇总,就可以了。
另外,要判断impossible,只需要加一个used数组,记录已经遍历了哪些点。如果重复遍历一个点,且与上一次的颜色不同,则必然是impossible的。
参考代码:
1 #include <iostream> 2 #include <cstring> 3 #include <cmath> 4 #include <cstdio> 5 #include <algorithm> 6 using namespace std; 7 inline int read(){ 8 char ch, c; 9 int res; 10 while (ch = getchar(), ch < ‘0‘ || ch > ‘9‘) c = ch; 11 res = ch - 48; 12 while (ch = getchar(), ch >= ‘0‘ && ch <= ‘9‘) 13 res = (res << 3) + (res << 1) + ch - 48; 14 if (c == ‘-‘) res = -res; 15 return res; 16 } 17 struct Edge{ 18 int t; 19 int nexty; 20 }edge[200000]; 21 int head[20000]; 22 int cnt = 0; 23 void add(int a, int b){ 24 cnt++; 25 edge[cnt].t = b; 26 edge[cnt].nexty = head[a]; 27 head[a] = cnt; 28 } 29 bool used[20000] = {0}; 30 int col[20000] = {0}; 31 int sum[2]; 32 bool dfs(int node, int color){ 33 if (used[node]){ 34 if (col[node] == color) return true; 35 else return false; 36 } 37 used[node] = true; 38 sum[col[node] = color]++; 39 bool tf = true; 40 for (int i = head[node]; i != 0 && tf; i = edge[i].nexty){ 41 tf = tf && dfs(edge[i].t, 1 - color); 42 } 43 return tf; 44 } 45 int main(){ 46 int n, m; 47 scanf("%d%d", &n, &m); 48 int a, b; 49 while (m--){ 50 scanf("%d%d", &a, &b); 51 add(a, b); 52 add(b, a); 53 } 54 int ans = 0; 55 for (int i = 1; i <= n; i++){ 56 if (used[i]) continue; 57 sum[0] = sum[1] = 0; 58 if (!dfs(i, 0)){ 59 printf("Impossible"); 60 return 0; 61 } 62 ans += min(sum[0], sum[1]); 63 } 64 printf("%d", ans); 65 return 0; 66 }