【无标题】

题目

886. 可能的二分法 - 力扣(LeetCode)

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

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

来源:力扣(LeetCode) 链接:力扣 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

贪心法(错误)

先向一个组合内进行尽可能地插入,不能插入当前组合的放入另一个集合,之后判断另一个集合里面的元素是否会发生排斥。

class Solution {
public:
    bool possibleBipartition(int n, vector<vector<int>>& dislikes) {
        unordered_map<int, unordered_set<int>> i2dislike;
        sort(dislikes.begin(), dislikes.end(), [](const auto& a, const auto& b){
            return a[0] < b[0];
        });
        int dislikeSize = dislikes.size();
        for(int i=0; i<dislikeSize; i++){
            i2dislike[dislikes[i][0]].insert(dislikes[i][1]);
        }
        unordered_set<int> cantAddA;
        for(int i=1; i<=n; i++){
            if (cantAddA.count(i) == 1){
                continue;
            }else{
                for(int t: i2dislike[i]){
                    cantAddA.insert(t);

                }
            }
        }
        unordered_set<int> cantAddB;
        for(auto it = cantAddA.begin(); it != cantAddA.end(); it++){
            int i = *it;
            if (cantAddB.count(i) == 1){
                return false;
            }else{
                for(int t: i2dislike[i]){
                    cantAddB.insert(t);
                }
            }
        }

        return true;
    }
};

错误原因

考虑dislike组合:[1,3], [3, 4], [4, 2]

根据贪心算法,我们会先加入[1, 2]之后[3, 4]无法插入,且[3, 4]内存在排斥;

但是[1, 4]和[3, 2]是合法。

出现错误的原因是,我们将一个不受限制的元素2默认插入到[1]中,但是2也能不放入[1],放到另一个集合中。我们没有考虑这种情况。

深度优先搜索

  • 插入某个节点后,我们将其互斥的节点插入到另一个分组中;

  • 递归进行这个步骤,

结束条件:

  • 存在互斥节点没有分组,return 递归(互斥节点)

  • 没有互斥的节点,return true;

  • 互斥的节点被插入另一组,continue;

  • 互斥的节点被插入同一组,return false;

为什么这里一旦发现互斥节点插入同一组就不行呢?

因此我们是按照dislike进行递归的,所有dislike的节点必须处在另一组,我们没有考虑不受控的节点,例如上个例子中的2。

上个例子的递归步骤是:

第一组第二组
递归入口1->(3)
13->(4)
1,4->(2)3
3, 2
class Solution {
public:
    bool dfs(int curnode, int nowcolor, vector<int>& color, const vector<vector<int>>& g) {
        // color[i] = 0 / 01 / 10
        color[curnode] = nowcolor;
        parts[nowcolor].push_back(curnode);
        for (auto& nextnode : g[curnode]) {  // 当前节点排斥的节点
            // 排斥的节点已经被分到同一组
            if (color[nextnode] && color[nextnode] == color[curnode]) {
                return false;
            }
            // 排斥的节点没有分组,将其分到另一组
            if (!color[nextnode] && !dfs(nextnode, 3 ^ nowcolor, color, g)) {
                return false;
            }
        }
        return true;
    }
​
    bool possibleBipartition(int n, vector<vector<int>>& dislikes) {
        vector<int> color(n + 1, 0);
        vector<vector<int>> g(n + 1);
        for (auto& p : dislikes) {
            g[p[0]].push_back(p[1]);
            g[p[1]].push_back(p[0]);
        }
        for (int i = 1; i <= n; ++i) {
            if (color[i] == 0 && !dfs(i, 1, color, g)) {
                return false;
            }
        }
        return true;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值