二分图定义与判定
在做这道题时,首先我们先了解二分图的基本概念。
二分图定义:
将图里的顶点分为两个集合,且集合内的点没有直接关联(也就是同一集合里的点不相连).
二分图性质
上图就是一个二分图,将图的所有顶点分为u,v集合. u,v集合互不相连.
我们先从二分图的性质出发:
1.能形成回路的:其回路长度必然是偶数。
2.不能形成回路的:无要求
3.不管从什么点出发必然都是u-v v-u (或者:v-u u-v)这样来回循环。 也就是一个点的左右邻边是与他相对的集合里的点。
从上图我可以发现有以下特点:
能形成回路:那么长度必然是偶数?
为什么呢:我选中3-9-5-6-3 这是回路。 3,5,3点属于u集合,9,6属于v集合.(抽象一下u-v-u-v-u)
在回路中一直是u-v v-u 这样的一对状态(两条边—偶数)。 所以长度必然是偶数. 得证.
2,3结论是显然的
二分图的判定(针对无向图分析)
什么样的图才满足二分图呢?
我们知道树上多加些边就能变成图。(当然树本来就是图,但图不一定是树).
下图就是二分图。
ps:而且两个集合的顶点 都是唯一的,就是不存在两个集合存放顶点有多种可能的情况(证明:从二分图定义出发,反证)。
两个集合:1和-1集合 。
左边是树每一层的编号.
上图的图是完整的二分图。
可以知道:所有奇数编号组成的点是一个集合,所有偶数组成的编号是另一个集合。
为什么呢? 用到了二分图的第三个结论:不管从什么点出发必然都是u-v v-u (或者:v-u u-v)这样来回循环。 也就是一个点的左右邻边是与他相对的集合里的点.
但是呢?下图就是不是二分图: 1,6的边是同一集合,二分图的定义同一集合的点两两互不相连。
所有不成立.
通过上面的分析:
总结一句:父亲节点的标记和儿子节点的标记相对。 不相对就不是二分图。
我们怎么判定是不是二分图呢?
dfs深度搜索: 同一层的标记一样的(1),其上一层,下一层标记一样的(-1).
在dfs中如果,父亲找儿子,儿子如果标记了而且和父亲的标记相同,必然不是二分图。儿子没被标记:赋予父亲标记的相对值。
题意:
有N台电脑相互连接,(但不一定连接),要求必须每条网线俩端的电脑必须有且只有一台电脑安装了更新,问需要在多少台电脑上面安装更新,如果不可能达到要求输出-1。
思路:一条边的只有一个点更新。点的事件只用两种可能:更新与未更新。
我们的二分图也是这样,一条边的左右点是是相对的集合。
那不恰好是问:是不是二分图,找出一个集合的顶点个数最小。
代码如下:
#include<iostream>
#include<algorithm>
#include<math.h>
#include<cstring>
#include<vector>
#include<queue>
#define ll long long
ll gcd(ll a,ll b){ return b? gcd(b,a%b):a;}
const int N=3e5+10;
const ll mod=1e9+7;
ll read(){
ll s = 0, f = 1; char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)) s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
return s * f;
}
using namespace std;
int n, m;
vector<int> a[N];
int flag[N];
int sum = 0;
// u 节点 ans 标记(1,-1)
bool dfs(int u,int ans){
flag[u]=ans;
// sum 存放 1集合的顶点个数。
if(ans==1)
sum++;
for (int i = 0; i < a[u].size();i++){
int v = a[u][i];
//父亲和儿子节点 标记相同 不行,直接退出
if(flag[v]==ans)
return false;
//对立的
if(flag[v]==-ans)
continue;
// 未被标记的。
if(flag[v]==0){
//如果 儿子失败直接退出
//也是 if(flag[v]==ans) return false; 的相呼应.
//递归就是这么好玩, 这里其实是相互呼应。
if(!dfs(v,-ans))
return false;
}
}
return true;
}
void solve(){
n = read();
m = read();
int u, v;
for (int i = 1; i <= m;i++){
u = read();
v = read();
a[u].push_back(v);
a[v].push_back(u);
}
for (int i = 1; i <= n;i++){
if(!flag[i]){
if(!dfs(i, 1)){
cout << -1;
return;
}
}
}
sum = min(sum, n - sum);
cout << sum << endl;
return;
}
int main (){
//while(1)
solve();
return 0;
}