洛谷P1330《封锁阳光大学》
原更新日期:2019-01-23 10:22:04
对子连通图的染色
题目描述
曹是一只爱刷街的老曹,暑假期间,他每天都欢快地在阳光大学的校园里刷街。河蟹看到欢快的曹,感到不爽。河蟹决定封锁阳光大学,不让曹刷街。
阳光大学的校园是一张由N个点构成的无向图,N个点之间由M条道路连接。每只河蟹可以对一个点进行封锁,当某个点被封锁后,与这个点相连的道路就被封锁了,曹就无法在与这些道路上刷街了。非常悲剧的一点是,河蟹是一种不和谐的生物,当两只河蟹封锁了相邻的两个点时,他们会发生冲突。
询问:最少需要多少只河蟹,可以封锁所有道路并且不发生冲突。
输入输出格式
输入格式
第一行:两个整数N,M
接下来M行:每行两个整数A,B,表示点A到点B之间有道路相连。
输出格式
仅一行:如果河蟹无法封锁所有道路,则输出“Impossible”,否则输出一个整数,表示最少需要多少只河蟹。
输入输出样例
输入样例#1
3 3
1 2
1 3
2 3
输出样例#1
Impossible
输入样例#2
3 2
1 2
2 3
输出样例#2
1
说明
【数据规模】
1<=N<=10000,1<=M<=100000,任意两点之间最多有一条道路。
解题思路
本题的图可能不为连通图(注意这个坑)
阅读题目,我们得到了这样几条信息:
「当某个点被封锁后,与这个点相连的道路就被封锁了」
「当两只河蟹封锁了相邻的两个点时,他们会发生冲突」
「封锁所有道路并且不发生冲突」
总结一下就是:
「要求每一条边有且仅有一个点被选择,求最少能选择多少点」
然后我们就可以考虑用染色的方法做这一题
我们枚举每一个点,以当前枚举到的起点为根对这个子连通图进行 DFS 染色(因为图可能不联通),答案累加每次染色的最小数量(黑色点数量和白色点数量中最小的)
代码实现
/* -- Basic Headers -- */
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
/* -- STL Iterators -- */
#include <vector>
#include <string>
#include <stack>
#include <queue>
/* -- External Headers -- */
#include <map>
#include <cmath>
/* -- Defined Functions -- */
#define For(a,x,y) for (int a = x; a <= y; ++a)
#define Forw(a,x,y) for (int a = x; a < y; ++a)
#define Bak(a,y,x) for (int a = y; a >= x; --a)
#define head(a) Head[a].id
#define nowcolor(a) Head[a].color
#define visited(a) Head[a].used
// 这样 define 有助于简化代码
namespace FastIO {
inline int getint() {
int s = 0, x = 1;
char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-') x = -1;
ch = getchar();
}
while (isdigit(ch)) {
s = s * 10 + ch - '0';
ch = getchar();
}
return s * x;
}
inline void __basic_putint(int x) {
if (x < 0) {
x = -x;
putchar('-');
}
if (x >= 10) __basic_putint(x / 10);
putchar(x % 10 + '0');
}
inline void putint(int x, char external) {
__basic_putint(x);
putchar(external);
}
}
namespace Solution {
int n, m, ans;
int sum0, sum1;
struct Graph {
static const int MAXN = 10000 + 10;
static const int MAXM = 100000 + 10;
struct Node {
int color, used, id;
// 在一个数组中存储三个数量
Node() { color = used = id = 0; }
} Head[MAXN];
struct Edge {
int now, next;
} edge[MAXM * 2];
int cnt;
inline void addEdge(int prev, int next, bool isR = true) {
if (isR) { addEdge(next, prev, false); }
edge[++cnt].now = next;
edge[cnt].next = head(prev);
head(prev) = cnt;
}
inline bool Color(int __id, int nowColor) {
// 返回 true 为成功染色, false 反之
if (visited(__id)) {
return nowcolor(__id) == nowColor;
// 如果当前被染过不同的颜色,就失败
}
visited(__id) = true;
nowcolor(__id) = nowColor;
if (nowColor) ++sum1;
else ++sum0;
bool __ans = true;
for (int e = head(__id); e && __ans; e = edge[e].next) {
int now = edge[e].now;
__ans = __ans & Color(now, nowColor ^ 1);
// 遍历与当前点相连的每一条边并 DFS
}
return __ans;
}
} g1;
void __EXIT() {
puts("Impossible");
exit(0);
}
}
signed main() {
#define HANDWER_FILE
#ifndef HANDWER_FILE
freopen("testdata.in", "r", stdin);
freopen("testdata.out", "w", stdout);
#endif
using namespace Solution;
using FastIO::getint;
n = getint();
m = getint();
For (i, 1, m) {
int prev = getint();
int next = getint();
g1.addEdge(prev, next);
}
for (int i = 1; i <= n; ++i) {
if (g1.visited(i)) continue;
sum0 = sum1 = 0;
if (!g1.Color(i, 0)) __EXIT();
ans += std::min(sum0, sum1);
}
FastIO::putint(ans, '\n');
return 0;
}