1 条题解
-
0
问题重述
给定一个长度为 ()的字符串 ,仅包含小写字母
a~f。你可以任意交换字符串中的字母(相当于可以任意重排)。同时,有 个位置限制:某些位置只能放置特定的字母集合(每个位置允许的字母是a~ `f$ 的一个非空子集)。没有限制的位置可以放置任何字母。目标:找到一个重排后的字符串,满足所有位置限制,并且字典序最小。如果不存在,输出
"Impossible"。
算法核心思想
由于字母种类只有 种,我们可以将问题转化为网络流 + 贪心构造。
第一步:建模为二分图匹配 / 网络流
- 左侧节点:字母种类(
a~ `f$),共 个节点。 - 右侧节点:位置允许集的类型(即
a~ `f$ 的所有非空子集),共 种类型(代码中用了 种,包含空集但不会出现)。 - 源点 连向每个字母节点 ,容量 = 原字符串中该字母的出现次数。
- 每个字母节点 连向所有包含该字母的允许集节点 ,容量为 (代码中取 足够大)。
- 每个允许集节点 连向汇点 ,容量 = 有多少个位置要求恰好这个允许集。
求出最大流。如果最大流 ,说明无法满足所有位置,输出
"Impossible"。
第二步:贪心构造最小字典序
关键观察:字典序最小意味着从左到右每个位置尽可能放小的字母。
我们按位置 到 依次决定字母:
- 对于当前位置 ,其允许集为 。
- 尝试字母
'a'到'f'中允许的字母。 - 假设我们在位置 放入字母 (满足 ),我们需要检查剩余部分是否仍然有解。
检查方法:
我们从当前流网络中暂时扣除一个字母 ,并暂时扣除一个允许集 的容量,然后尝试寻找一条增广路。如果能找到增广路,说明剩余部分仍可完成匹配,就选择该字母;否则不能选该字母。
标程中的
check函数详解check(ch, mask)的作用:判断在当前残留网络中,能否将字母 分配给允许集 (即当前位置),且剩余部分仍能完美匹配。网络流节点编号
- 字母节点
a~f:编号 ~ 。 - 允许集节点 :编号 ~ 。
- 源点 ,汇点 。
check函数的执行流程- 合法性检查:若 不在 中,返回
false。 - 获取关键边:
- :源点 到字母节点 的边。
- :允许集节点 到汇点 的边。
- 如果 或 的当前流量为 :说明没有可分配的字母或该允许集已满,返回
false。 - 尝试扣除:先将 的流量减 (反向边加 ),表示暂用一个字母 。
- 寻找匹配路径:
- 从字母节点 出发,找一条正向边(非反向)且当前流量 ,走到某个允许集节点 ,将该边流量减 。
- 再从 出发,找一条正向边到汇点 ,将该边流量减 。
- 这样就构成了一条从 的路径,流量为 被暂时移除。
- 判断汇点边 的情况:
- 如果 当前流量 容量,说明 还有空位,直接扣减 和 各 容量,返回
true。 - 否则,需要释放一条已有的匹配来为 腾出空间。这要通过反向边增广来实现。
- 如果 当前流量 容量,说明 还有空位,直接扣减 和 各 容量,返回
- 反向边调整(复杂部分):
- 从 出发,找一条反向边(流量为负)到某个字母节点 ,将该边流量加 (即减少反向流量)。
- 再从 出发,找一条反向边到源点 ,同样调整。
- 这相当于在残留网络中找一条从 到 的路径,释放一条旧的匹配。
- 尝试增广:将 和 的容量各减 ,然后调用
dfs(s, 1)看能否找到增广路(即能否重新分配)。- 若能找到,返回
true。 - 若不能,恢复所有被修改的边,返回
false。
- 若能找到,返回
时间复杂度分析
- 最大流:,这里 ,,但每次增广是 ,总 ,可接受。
- 贪心构造:每个位置尝试最多 个字母,每次
check调用dfs一次,,总 ,在 2 秒内可勉强通过(常数小)。
示例验证
样例 1
bedefead 5 2 e 1 dc 5 b 7 ef 6 ef原串字母统计:
a:1, b:1, c:0, d:2, e:3, f:1
限制:- 位置 1(0-based):允许
dc→ mask{c,d}对应 - 位置 2:允许
e→ mask{e}对应 - 位置 4:允许
b→ mask{b}对应 - 位置 5:允许
ef→ mask{e,f}对应 - 位置 6:允许
ef→ mask{e,f}对应
网络流匹配后,贪心构造得到
deadbeef。样例 2
无限制,直接排序原串得到
aaaabbc。样例 3
fc 2 1 cfab 2 f位置 1:允许
c,f,a,b→ mask{a,b,c,f}位置 2:允许f原串有
f,c各一。贪心:- 位置 1 尝试
a不行,b不行,c可以 → 放c - 位置 2 只能放
f→ 得到cf
总结
该题是网络流 + 贪心构造的经典应用。关键点:
- 用 位掩码表示允许集,建图做最大流判断可行性。
- 贪心时用
check函数在残留网络中判断能否分配当前字母。 - 注意网络流边的方向、正向/反向边的处理,以及增广路的查找。
本题的难点在于
check函数中对已有匹配的调整,需要理解残留网络的反向边作用。 - 左侧节点:字母种类(
- 1
信息
- ID
- 6543
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 6
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者