题解 P1330 【封锁阳光大学】
EricWay1024 · · 题解
扬中夏令营Day0的晚上练手写一道题。还比较顺利吧...
思路:
这就是个染色问题。把图上的点染成红色和蓝色。要求:同一条边上的两个点颜色不能一样。
解释一下:不妨红色对应河蟹占领而蓝色对应没有占领。
-
如果一条边两个点都是红色,那就是两个河蟹打架了
-
如果一条边两个点都是蓝色,那就这条边没有被切断
上面两种情况是不行的。
- 如果一条边两个点一红一蓝,那就是恰好被切断
这种情况是可以的。
对吧?
如此看来,所谓占领和不占领其实是等价的。
在一个连通图中,令任意一个点是红色或者蓝色,那么整张图的颜色应该已经确定了。为了让红色的点最少,只需要分别尝试两种情况(令这个点是红色或者令这个点是蓝色),看看哪种情况中红点数目最少即可。
为什么会Impossible?那是因为,在从第一个点的颜色一直推到所有点的颜色的过程中,有的点会被要求涂上相反的颜色。即,当你想把一个点涂成蓝色的时候,却发现它已经是红色了。这就出现无解情况。
在下面的代码里,你会看到一个叫dfs的函数。它的功能是:令i的颜色是s(s∈{0,1}),返回i所在的连通图用了颜色1的点数。
是的。连通是个永恒的话题。千万不要忘记对不连通的考虑。在下面的代码里,我使用了一个循环来解决这个问题。注意被染过色的点不需要继续遍历。
对color2数组的解释:因为我太笨了,实在不知道怎么样才能让dfs(i,0)和dfs(i,1)并行不悖。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<string>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stack>
#include<set>
using namespace std;
vector<int> g[10000+10];
int color[10000+10];
int color2[10000+10];
int n, m;
bool ok;
int dfs(int i, int s){
if (!ok) return 0;
int sum=s;
color[i]=s;
for (int j=0; j<g[i].size(); j++){
int v=g[i][j];
if (color[v]==-1){
color[v]=(!s);
sum+=dfs(v, !s);
} else if (color[v]==s) ok=false;
}
return sum;
}
int main(){
cin>>n>>m;
for (int i=0; i<m; i++){
int u, v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
memset(color, -1, sizeof(color));
ok=true;
int one=0;
int ans=0;
for (int i=1; i<=n; i++){
if (color[i]==-1){
memcpy(color2, color, sizeof(color));
int a=dfs(i,0);
memcpy(color, color2, sizeof(color));
int b=dfs(i,1);
ans+=min(a,b);
}
if (!ok) break;
}
if (!ok){
cout<<"Impossible"<<endl;
} else {
cout<<ans<<endl;
}
return 0;
}