Possible Bipartition 可能的二分法

给定一组 N 人(编号为 1, 2, ..., N), 我们想把每个人分进任意大小的两组。

每个人都可能不喜欢其他人,那么他们不应该属于同一组。

形式上,如果 dislikes[i] = [a, b],表示不允许将编号为 a 和 b 的人归入同一组。

当可以用这种方法将每个人分进两组时,返回 true;否则返回 false

示例 1:

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

示例 2:

输入:N = 3, dislikes = [[1,2],[1,3],[2,3]]
输出:false

示例 3:

输入:N = 5, dislikes = [[1,2],[2,3],[3,4],[4,5],[1,5]]
输出:false

提示:

  1. 1 <= N <= 2000
  2. 0 <= dislikes.length <= 10000
  3. 1 <= dislikes[i][j] <= N
  4. dislikes[i][0] < dislikes[i][1]
  5. 对于 dislikes[i] == dislikes[j] 不存在 i != j 

思路:这道题的解题思路和Is Graph Bipartite 判断二分图完全一样,分别可以采用DFS和BSF来做,下面分别介绍这两种做法。

无论是用BFS还是DFS,我们都需要把题意转化成图结构,首先这是一个无向边图,所以我们构建邻接表时要注意,其次这里的dislike其实就是边的组合,我们在染色的时候出现在dislike中的组合就应该染成不同的颜色。无论DFS还是BFS,规定以下颜色规律:-1未染色,0灰色,1黑色

方法一:BFS,在一次BFS中,对于每个节点,我们首先判断其是否未染色,只有未染色才进入,然后需要一个辅助队列q,把当前节点放入辅助队列中,如果队列不为空,则取出队列的首元素,染成0号颜色(初始状态),然后对于其邻接点,全部染成其他号颜色,如果遇到某邻接点已经是其他号颜色,直接返回false,如果已经染成其他号颜色则跳过。对于染成其他号颜色的节点放入队列中,一直循环。

参考代码:

class Solution {
public:
vector<unordered_set<int>> make_graph(vector<vector<int>>& dislikes, int N) {
	vector<unordered_set<int>> graph(N + 1);
	for (int i = 0; i < dislikes.size(); i++) {
		graph[dislikes[i][0]].insert(dislikes[i][1]);
		graph[dislikes[i][1]].insert(dislikes[i][0]);
	}
	return graph;
}
bool possibleBipartition(int N, vector<vector<int>>& dislikes) {
	vector<int> colors(N + 1, -1);
	vector<unordered_set<int>> graph = make_graph(dislikes, N);
	queue<int> q;
	for (int i = 1; i <= N; i++) {
		if (!graph[i].empty() && colors[i] == -1) {
			q.push(i);
			colors[i] = 0;
			while (!q.empty()) {
				auto source = q.front(); q.pop();
				for (auto neibor : graph[source]) {
					if (colors[neibor] != -1 && colors[neibor] == colors[source]) return false;
					if (colors[neibor] == -1) {
						colors[neibor] = colors[source] == 0 ? 1 : 0;
						q.push(neibor);
					}
				}
			}
		}
	}
	return true;        
    }
};

方法二:DFS,

对于每个节点:

  1. 如果他还没有被染色,使用一个颜色去染色. 然后使用其他颜色去染色他所有的邻接点 (DFS).
  2. 如果已经被染色了, 检查当前的颜色是不是应该被染成的正确颜色. 
class Solution {
public:
vector<unordered_set<int>> make_graph(vector<vector<int>>& dislikes, int N) {
	vector<unordered_set<int>> graph(N + 1);
	for (int i = 0; i < dislikes.size(); i++) {
		graph[dislikes[i][0]].insert(dislikes[i][1]);
		graph[dislikes[i][1]].insert(dislikes[i][0]);
	}
	return graph;
}
bool validColor(vector<unordered_set<int>> &graph,int source,int color, vector<int> &colors) {
	if (colors[source] != -1) return colors[source] == color;
	colors[source] = color;
	for (auto neibor : graph[source]) {
		if (!validColor(graph, neibor, 1 - color, colors)) return false;
	}
	return true;
}
bool possibleBipartition(int N, vector<vector<int>>& dislikes) {
	vector<int> colors(N + 1, -1);
	vector<unordered_set<int>> graph = make_graph(dislikes, N);
	for (int i = 1; i <= N; i++) {
		if (colors[i] == -1 && !validColor(graph,i,0, colors)) {
			return false;
		}
	}
	return true;
}
};

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值