【二分图】886. 可能的二分法

题目描述

给定一组 n 人(编号为 1, 2, ..., n), 我们想把每个人分进任意大小的两组。每个人都可能不喜欢其他人,那么他们不应该属于同一组。

给定整数 n 和数组 dislikes ,其中 dislikes[i] = [ai, bi] ,表示不允许将编号为 aibi的人归入同一组。当可以用这种方法将所有人分进两组时,返回 true;否则返回 false

示例:

输入:n = 4, dislikes = [[1,2],[1,3],[2,4]]
输出:true
解释:group1 [1,4], group2 [2,3]

解题思路

本题是很经典的二分图题目,如果之前没有接触过二分图,本题还是挺有难度的。

单从图算法题的角度出发,大体上有两种思路:

  1. 类似于并查集、岛屿问题,遍历所有人后统计剩余的连通分量个数;
  2. 先分成两组,在考虑放人进组,若分人过程顺利则为真,即二分图

第一个思路的难点在于:遍历起点不同,最后连通分量数目可能不同。即,可能因为遍历顺序导致原本可以分为两组的情况,被分为更多组!

第二个思路实现起来相对简单:

  1. 根据dislikes[]构建邻接链表graph
  2. 分为红蓝两组;
  3. 初始化color[]数组表示各结点尚为染色:下标为结点编号,数组值为颜色0 -- 为染色、1 -- 红色、2 -- 蓝色
  4. 随机结点开始染为红色,之后遍历所有未染色的结点,之所以要遍历是因为graph可能不是连通图!
  5. 根据邻接链表graph对邻居结点染色:对尚未染色的结点染当前结点相反的颜色、对已染色的结点校验本此着色是否于当前颜色冲突;
  6. 存在冲突立即返回false;
  7. 完成所有结点染色则返回true

直接上代码,下有更详细的注释。

代码实现

class Solution {
public:
    bool possibleBipartition(int n, vector<vector<int>>& dislikes) {
        //构建邻接链表
        vector<vector<int>> graph(n + 1);
        for(auto& vec : dislikes){
            graph[vec[0]].push_back(vec[1]);
            graph[vec[1]].push_back(vec[0]);
        }
        //初始化color,0表示为染色,1为红色,2为蓝色
        vector<int> color(n + 1);
		//随机遍历!为方便实现,从1开始顺序遍历
        for(int i = 1; i <= n; i++){
        	//只有未染色结点才有必要初始赋红色,只有dfs()内发生冲突才返回false
            if((color[i] == 0) && !dfs(graph, color, i, 1)) return false;
        }
        //无冲突
        return true;
    }

    bool dfs(vector<vector<int>>& graph, vector<int>& color, int id, int colour){
        //染色结点已被染色!校验颜色
        if(color[id] != 0) return color[id] == colour;
        //结点未染色,为结点染对应的颜色
        color[id] = colour;
        //邻居节点应染为对立颜色
        int neibourColor = colour == 1 ? 2 : 1;
        //根据邻接链表为邻居结点染色
        for(int& i : graph[id]){
            if(!dfs(graph, color, i, neibourColor)) return false;
        }
        return true;
    }
};

运行结果:
result

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值