📜  门|门CS 2010 |第 48 题(1)

📅  最后修改于: 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 $$

实现步骤
  1. 给每扇门编号,用二进制数存储每扇门的状态。
  2. 建立二维数组 $s$,记录每个开关能够控制哪些门。
  3. 计算每个状态的 $state$ 值,此处使用动态规划,状态转移方程为 $state_i=\min\limits_{j\in{s_i}} state_{i-j}+j$。
  4. 从最终状态逆推,找到字典序最小的一种方案。
代码实现
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]))
参考链接

AcWing 1593. 门