【二分图】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

猜你喜欢

转载自blog.csdn.net/LogosTR_/article/details/125630703