题-J. Burnished Security Updates(二分图)

二分图定义与判定

在做这道题时,首先我们先了解二分图的基本概念。

二分图定义:

将图里的顶点分为两个集合,且集合内的点没有直接关联(也就是同一集合里的点不相连).

二分图性质

在这里插入图片描述

上图就是一个二分图,将图的所有顶点分为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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

axtices

谢谢您的打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值