贪心是一种很早就学了但是直到最后还是
可能做不出来的算法
# 题面
> Linked 洛谷 P6775
# 解析
线上赛的时候就注意到 $m\ge n-2$ 有鬼……但是还是没想出来。既然是贪心,就会涉及到一大堆证明。按照部分分一步步推导:
# Part1 - $m=n-1$
先把 $\{d_i\}$ 从小到大排序。
定理 1
若 $m=n-1$,则 $d_1< k$。
若 $d_1\ge k$,则 $d_i\ge d_1\ge k$,$\sum d_i\ge nk> (n-1)k=mk$;但是 $\sum d_i=mk$,矛盾。
定理 2
若 $m=n-1$ 且 $n>2$,则 $d_1+d_n> k$。
仍然反证,若 $d_1+d_n\le k$,则有 $d_i\le d_n< k$,$d_2+d_3+\dots+d_{n-1}+(d_1+d_n)< (n-1)k=mk$,与 $\sum d_i=mk$ 矛盾。
这意味着什么?对于 $m=n-1$ 的情况:
- 若 $n>2$ 则直接找到最大和最小、把最小用完、再拿大的补,这样一定可以有合法的方案;并且递归到 $m’=m-1,n’=n-1$ 的情况——因为做出了一道菜(
m--
),而且最小的用完了(n--
),注意到因为 $d_1+d_n>k$,所以最大的不会用完。 - 若 $n=2$ 直接两个匹配。
# Part2 - $m>n-1$
定理 3
若 $m>n-1$,则 $d_n\ge k$。
因为 $m,n$ 是整数,条件相当于 $m\ge n$。
和定理1证明很像,若 $d_n< k$ 则 $d_i\le d_n< k$,$\sum d_i< nk\le mk$,与 $\sum d_i=mk$ 矛盾。
对于这种情况,可以先只用最大的 $d_n$ 做出一道菜,则归入 $m’=m-1$ 的子问题;注意到有可能 $k\mid d_n$,此时 $d_n$ 可能用完,需要归入 $n’=n-1$ 的子问题。
直到 $m=n-1$,一定有解。
# Part3 - $m=n-2$
定理 4
当 $m=n-2$ 时,$\{d_i\}$ 能被划分为两个集合 $S_1,S_2$ 使得 $(|S_1|-1)k=\operatorname{sum}(S_1)$ 且 $(|S_2|-1)k=\operatorname{sum}(S_2)$ 是有解的充要条件。
其实就是把 $m=n-2$ 划分成两个 $m=n-1$ 的问题求解。如果能划分出来,那么一定有解(充分性);接下来要证明有解的情况一定可以划分出来(必要性)。
这里比较巧妙的一种一种理解是建图——若一个菜用了两个食材 $a,b$,则连边 $(a,b)$。
在 $m=n-1$ 的问题中,最后一步操作用完了两个食材做出了一道菜。此时 $m’=m-1,n’=n-2$,则 $n,m$ 之间的差减少 $1$,在此之前只有“用完最小值和部分最大值”,$m’=m-1,n=n-1$,$m,n$ 差值不变。
综上,一个 $m=n-1$ 的子问题会让 $m,n$ 差值减少 $1$。
而问题最后要让 $m=n=0$,则 $m,n$ 的差值在操作过程中必须减二,也就对应了两个 $m=n-1$ 的问题。
划分可以背包,$f_{i,S}$ 表示前 $i$ 个 $d_i$,选择了一个非空子集 $T$(正因为是非空,所以一开始就把 $d_1$ 固定选在 $T$ 内),有 $S=\sum_{i\in T}(k-d_i)$,是否存在方案。
因为是 bool
类,所以可以用 bitset 优化,而判断是否有解只需要看 $f_{n,k}$。
最后再用背包问题求一个可行解的方法来找到合法的划分,做两次 $m=n-1$ 的问题。
# 源代码
1 | /*Lucky_Glass*/ |
THE END
Thanks for reading!
> Linked 零和 Zero-Sum-Bilibili