📅  最后修改于: 2023-12-03 15:12:47.200000             🧑  作者: Mango
有 $n$ 个开关,每个开关可以控制某些门的状态,第 $i$ 个开关能够控制 $a_{i1},a_{i2} \cdots a_{ik_i}$ 这些门,将这些开关状态按照输入顺序合并,若最终每扇门均处于关闭状态,则输出一种控制方案。若有多种方案则输出字典序最小的一种。
第一行包含两个整数 $n,m$。
接下来 $n$ 行,每行表示一个开关的控制门列表,第一个数 $k_i$ 表示这个开关能够控制的门的数量,接下来 $k_i$ 个数 $a_{i1},a_{i2} \cdots a_{ik_i}$,表示该开关能够同时控制这 $k_i$ 扇门。
保证每扇门最多被一个开关控制。
共 $m$ 行,每行为一种控制方案,按照输入顺序输出开关编号,用空格隔开,若有多种方案则输出字典序最小的一种。
输入:
5 5
2 1 2
2 2 3
1 4
1 4
1 4
输出:
1 2 4
2 4
3 4
4
这是一道比较经典的状态压缩的题目,我们可以用二进制数表示每扇门的状态,然后用一个二维数组 $s$ 来表示第 $i$ 个开关能够控制哪些门。设 $state_i$ 表示状态为 $i$ 时,所有门的状态均为关闭状态时所需的最小开关编号和。换句话说,$state_i$ 包含的所有门的状态都应该是关闭状态,且开关编号和最小。
我们可以首先计算出所有 $state_i$,然后由最终状态向前倒推得到一种字典序最小的方案。
状态转移方程:
$$ state_i=\min\limits_{j\in{s_i}} state_{i-j}+j $$
n, m = map(int, input().split())
doors = {} # 门的状态
states = {} # 每个状态的最小开关编号和
switchs = [[] for _ in range(n)] # 每个开关能够控制的门
for i in range(n):
raw = list(map(int, input().split()))
for j in raw[1:]:
if j not in doors:
doors[j] = 0
doors[j] |= 1 << i # 第i个开关能够控制第j扇门
switchs[i].append(j)
final_state = 0 # 最终状态
for k in doors:
final_state |= doors[k]
for i in range(1, final_state + 1):
states[i] = float('inf')
for i in range(1, 1 << n):
valid = True
for k in doors:
if i & doors[k] == doors[k] and doors[k] != 0:
valid = False
break
if not valid:
continue
for j in range(n):
if i & (1 << j) == 0:
continue
for door in switchs[j]:
states[i] = min(states[i - (1 << j)], states[i - (1 << j) & ~doors[door]] + j)
print(states[final_state])
cur_state = final_state
ans = []
while cur_state:
for i in range(n):
if cur_state & (1 << i):
next_state = cur_state - (1 << i)
for door in switchs[i]:
if next_state & ~doors[door] == next_state:
if states[cur_state] == states[next_state] + i:
ans.append(str(i + 1))
cur_state = next_state
break
break
print(' '.join(ans[::-1]))