📅  最后修改于: 2023-12-03 15:12:47.228000             🧑  作者: Mango
题目链接:门|门CS 2010 |第 58 题
有一排门,每个门可以是“开”或“关”。从左到右数第i个门和第i+1个门可以用一个开关来控制它们的状态,如果状态为“关”,则互相之间会有一扇墙。
现在有n个人要进入这一排门中的一些门(一个人只能从门的一侧进入)。由于人太多,他们决定两个人之间必须隔一个门进入,即如果第i个人和第i+1个人要进入同一个门则他们必须从不同的一侧进入,具体来说,如果第i个人要进入第k个门(仅当第k个门是开着的)第i+1个人只能进入第k+2个门。
现在给出m个限制,限制有两种形式:
第i个人和第j个人必须隔k个门进入;(x[1] < x[2])
第i个人和第j个人必须隔k个门进入,并且之间必须有一扇门开着;
请问这n个人能否进入这n个门?如果能,输出一种方案。
第一行两个整数m,n。
接下来m行,每行描述一个限制,格式为x[1] x[2] k t,表示两个人之间必须隔k个门进入,如果t=1,则之间必须有一扇门开着。
如果无解,则输出-1。
否则按顺序输出每个人从哪个门进入,每个数占一行,第i个数表示第i个人从哪个门进入。
1≤m≤105,
1≤n,k≤100,
1≤x[1],x[2]≤n,
t=0或1.
这是一道十分经典的建图问题,我们可以将其转换为图模型,并通过染色方法解决。
首先想到的是用一个二分图表示门和人的位置,但是由于门之间存在关联,二分图并不能满足限制,我们需要将人和门分开处理。
由于两个人之间必须隔k个门进入,我们考虑将门看做节点, 如果两个人之间必须隔k个门进入,则他们之间的所有门节点之间没有边连在一起。如果两个人之间必须隔k个门进入,并且之间必须有一扇门开着,则这两个人之间的所有门节点之间没有边连在一起,除了其中一扇门的两个节点之间存在一条有向边。
我们将这些门节点作为左部的节点,人作为右部的节点,如果一个人可以从某个门进入,则从该人节点向它可以进入的门节点连有向边。需要注意的是,由于人之间必须隔一个门进入,因此我们需要将同侧不可进入的门节点看做一组,将其用一个虚拟节点代表,然后将该虚拟节点与其他门节点连边。最后再加上源和汇节点,并将源节点与所有人节点连边,将所有可进入的门节点与汇节点连边。最后我们通过染色方法来求出图的一个可行解。
该题代码实现比较复杂,需要使用邻接表存储建图,同时求解的过程中需要涉及到虚拟节点的映射,编写代码时需要注意细节问题。
# 门|门CS 2010 |第 58 题
# 使用邻接表存储建图,代码实现比较复杂,需要注意细节问题。
class Solution:
def __init__(self):
self.INF = 0x3f3f3f3f
self.color = []
self.head = []
self.edge = []
def dfs(self, u):
self.color[u] = 1
for i in range(self.head[u], -1, -1):
v, w = self.edge[i]
if self.color[v] == 1:
return False
if self.color[v] == 0 and not self.dfs(v):
return False
self.color[u] = 2
return True
def addEdge(self, u, v, w):
self.edge.append((v, w, self.head[u]))
self.head[u] = len(self.edge) - 1
def topoSort(self, n):
self.color = [0] * (n + 1)
for i in range(n):
if self.color[i] == 0 and not self.dfs(i):
return False
return True
def solve(self, n, m, edgeList):
# 构建图
self.edge.clear()
self.head = [-1] * (n * 3 + 2)
s, t = n * 3, n * 3 + 1
for i in range(n):
self.addEdge(s, i, 1)
self.addEdge(i + n * 2, t, 1)
# 人分组
for i in range(n):
self.addEdge(i, i + n, 1)
self.addEdge(i + n, i, 0)
# 门分组
for i in range(1, n + 1):
self.addEdge(i + n, i + n * 2, 1)
self.addEdge(i + n * 2, i + n, 0)
# 添加关系图
for i in range(m):
a, b, k, flg = edgeList[i]
x, y = 2 * a - 1, 2 * b - 1
if flg:
for j in range(max(1, k - n), min(k + 1, n)):
if j != a and j != b:
self.addEdge(j + n * 2, x + 1, self.INF)
self.addEdge(j + n * 2, y + 1, self.INF)
else:
for j in range(max(1, k - n), min(k + 1, n)):
if j != a and j != b:
self.addEdge(x + 1, j + n, self.INF)
self.addEdge(y + 1, j + n, self.INF)
# 执行拓扑排序求解
if not self.topoSort(n * 3 + 2):
return -1
# 打印答案
res = []
for i in range(m):
a, b, k, flg = edgeList[i]
x, y = 2 * a - 1, 2 * b - 1
if self.color[x + 1] < self.color[y + 1]:
res.append(a)
else:
res.append(b)
return res
时间复杂度:$O(m n)$