题目解析
特殊说明
- 这篇博客是对于 论文 + 题解 的详细解释。
- 贪心做法是每次取能取货币中价值最大的货币。
- 每个引理下面都是证明。
定义
2
2
2 个系统(定义:一个序列的表示方法)
C
,
V
C, V
C,V。
C
=
(
c
1
,
c
2
,
.
.
.
,
c
n
)
C = (c_1, c_2, ... \ , c_n)
C=(c1,c2,... ,cn)(这是题目给出的货币系统,
c
i
=
a
i
c_i = a_i
ci=ai,即
c
i
>
c
i
+
1
c_i > c_{i+1}
ci>ci+1)。
V
=
(
v
1
,
v
2
,
.
.
.
,
v
n
)
V = (v_1, v_2, ... \ , v_n)
V=(v1,v2,... ,vn)(这是对于某一个价值
W
W
W 的一种构造方案,其中
v
i
v_i
vi 表示使用价值为
c
i
c_i
ci 的货币的数量)。
定义
V
=
c
1
×
v
1
+
c
2
×
v
2
,
.
.
.
,
c
n
∗
v
n
=
C
∗
V
V = c_1 \times v_1 + c_2 \times v_2, ... \ , c_n * v_n = C * V
V=c1×v1+c2×v2,... ,cn∗vn=C∗V。
设
G
(
W
)
G(W)
G(W) 表示对于价值
W
W
W 的 字典序最大 的 贪心 构造方案中(在计算中表示需要的货币数,下标表示第
i
i
i 个货币使用的数量,有时简写成
G
G
G)。
设
M
(
W
)
M(W)
M(W) 表示对于价值
W
W
W 的 字典序最大 的 最优 构造方案中(在计算中表示需要的货币数,下标表示第
i
i
i 个货币使用的数量,有时简写成
M
M
M)。
引理 0
- G ( W ) G(W) G(W) 的字典序一定 大于等于 M ( W ) M(W) M(W)。
- W W W 越小, G ( W ) G(W) G(W) 的字典序也越小。
因为贪心每次都是取最大的货币,越大的货币越靠前,贪心取的货币一定是达到了上限的,最优方案是不可能超过它的,第二个同理。
引理 1
- 设 W W W 的贪心构造方案为 V W = ( v 1 , v 2 , . . . , v n ) V_W = (v_1, v_2, ... \ , v_n) VW=(v1,v2,... ,vn),并记其中取的一个货币为 c i c_i ci。
- 一定有结论:贪心构造方案 V W − c i = ( v 1 , v 2 , . . . , v i − 1 , . . . , v n ) V_{W - c_i} = (v_1, v_2, ... \ , v_i - 1, ... \ , v_n) VW−ci=(v1,v2,... ,vi−1,... ,vn)(少取了 1 1 1 次 c i c_i ci), G ( W ) = G ( W − c i ) + 1 G(W) = G(W - c_i) + 1 G(W)=G(W−ci)+1。
证明前者:设 W W W 贪心取完 c 1 ∼ i − 1 c_{1 \sim i-1} c1∼i−1 剩下的值为 X X X,取走的总价值为 Y Y Y,所以 W = Y + X W = Y + X W=Y+X(因为能取走 c i c_i ci,所以 X ≥ c i X \geq c_i X≥ci),所以 W − c i = Y + X − c i W - c_i = Y + X - c_i W−ci=Y+X−ci, Y Y Y 的贪心方案肯定是相同的, X X X 和 X − c i X - c_i X−ci 又必须满足每次取最大(贪心),所以只能少取一个 c i c_i ci。
证明后者: G ( W ) G(W) G(W) 比 G ( W − c i ) G(W - c_i) G(W−ci) 只多取了 1 1 1 次 c i c_i ci,所以 G ( W ) = G ( W − c i ) + 1 G(W) = G(W - c_i) + 1 G(W)=G(W−ci)+1。
- 也可以证明最优构造方案也是如此(也满足前者和后者)
证明后者( M ( W ) = M ( W − c i ) + 1 M(W) = M(W - c_i) + 1 M(W)=M(W−ci)+1),因为 M ( W − c i ) M(W - c_i) M(W−ci) 也是一种最优方案,多了 1 1 1 个 c i c_i ci,最少 + 1 +1 +1, M ( W ) M(W) M(W) 也是一种最优方案,所以 M ( W ) = M ( W − c i ) + 1 M(W) = M(W - c_i) + 1 M(W)=M(W−ci)+1。
证明了后者,前者就显然了,因为字典序最大,所以只能少取 1 1 1 个 c i c_i ci,否则 M ( W ) M(W) M(W) 和 W ( W − c i ) W(W - c_i) W(W−ci) 都不是字典序最大的最优方案。
引理 2
- 若 G i ≠ 0 G_i \neq 0 Gi=0,则 M i = 0 M_i = 0 Mi=0。
- 若 M i ≠ 0 M_i \neq 0 Mi=0,则 G i = 0 G_i = 0 Gi=0。
设
W
W
W 是最小的反例(即使得
G
(
W
)
>
M
(
W
)
G(W) > M(W)
G(W)>M(W) 的最小
W
W
W)。
所以有
G
(
W
−
c
i
)
=
M
(
W
−
c
i
)
G(W - c_i) = M(W - c_i)
G(W−ci)=M(W−ci),否则
W
−
c
i
W - c_i
W−ci 会是更小的反例
也可以证明构造方案也是相同的,因为要满足字典序最大。
- G ( W − c i ) = M ( W − c i ) G(W - c_i) = M(W - c_i) G(W−ci)=M(W−ci)。
反证法,如果不满足,即存在
G
i
>
0
G_i > 0
Gi>0 且
M
i
>
0
M_i > 0
Mi>0。
则可以同时少取
1
1
1 个
c
i
c_i
ci,货币数同时
−
1
-1
−1,大小关系不会改变,这样
W
−
c
i
W - c_i
W−ci 会是更小的反例,所以一定满足。
引理 3
- G ( W − c j ) G(W - c_j) G(W−cj) 和 M ( W ) M(W) M(W) 的前 i − 1 i - 1 i−1 位相同(都为 0 0 0)。
- W − c j < c i − 1 W - c_j < c_{i-1} W−cj<ci−1。
设
i
i
i、
j
j
j 分别满足
c
1
∼
i
−
1
=
0
,
c
i
≠
0
c_{1 \sim i-1} = 0, c_i \neq 0
c1∼i−1=0,ci=0、
c
j
≠
0
,
c
j
+
1
∼
n
=
0
c_j \neq 0, c_{j+1 \sim n} = 0
cj=0,cj+1∼n=0。
因为
W
W
W 是最小的反例,所以
G
(
W
−
c
j
)
=
M
(
W
−
c
j
)
G(W - c_j) = M(W - c_j)
G(W−cj)=M(W−cj)(引理
2
2
2),而
M
(
W
−
c
j
)
M(W - c_j)
M(W−cj) 是
M
(
W
)
M(W)
M(W) 少取了一次
c
j
c_j
cj,只会影响到
v
j
v_j
vj,所以
M
(
W
−
c
j
)
M(W - c_j)
M(W−cj) 和
M
(
W
)
M(W)
M(W) 的前
i
−
1
i - 1
i−1 位相同(都为
0
0
0),所以
G
(
W
−
c
j
)
G(W - c_j)
G(W−cj) 和
M
(
W
)
M(W)
M(W) 的前
i
−
1
i - 1
i−1 位相同(都为
0
0
0)。
因为贪心是每次取最大的,又要满足
c
1
∼
i
−
1
c_{1 \sim i-1}
c1∼i−1 不取,所以
W
−
c
j
<
c
i
−
1
W - c_j < c_{i-1}
W−cj<ci−1。
引理 4
- G ( W ) G(W) G(W) 在 1 ∼ i − 1 1 \sim i-1 1∼i−1 上,一定有非零位。
- W ≥ c i − 1 W \geq c_{i-1} W≥ci−1。
反证法,假设 G ( W ) G(W) G(W) 在 1 ∼ i − 1 1 \sim i-1 1∼i−1 上全是零,又因为 M i ≠ 0 M_i \neq 0 Mi=0,由 引理 2 可知 G i = 0 G_i = 0 Gi=0,所以 G G G 的字典序 小于 M M M 的字典序,不满足 引理 0,假设不成立。
因为 G ( W ) G(W) G(W) 在 1 ∼ i − 1 1 \sim i-1 1∼i−1 上有非零位,就是至少在前面取一个货币,所以 W ≥ c i − 1 W \geq c_{i-1} W≥ci−1。
分析
- 特殊说明:以下比较均为字典序比较。
根据 引理 3,因为 W , c i − 1 W, c_{i-1} W,ci−1 都是整数,所以有 W − c j ≤ c i − 1 − 1 W - c_j \leq c_{i-1} - 1 W−cj≤ci−1−1,即 M ( W − c j ) = G ( W − c j ) ≤ G ( c i − 1 − 1 ) M(W - c_j) = G(W - c_j) \leq G(c_{i-1} - 1) M(W−cj)=G(W−cj)≤G(ci−1−1)(结合 引理 0, 2)。
根据 引理 4,有 W > c i − 1 − 1 W > c_{i-1} - 1 W>ci−1−1,所以 W − c i ≤ c i − 1 − 1 − c i W - c_i \leq c_{i-1} - 1 - c_i W−ci≤ci−1−1−ci,所以有 M ( W − c i ) = G ( W − c i ) > G ( c i − 1 − 1 − c i ) M(W - c_i) = G(W - c_i) > G(c_{i-1} - 1 - c_i) M(W−ci)=G(W−ci)>G(ci−1−1−ci),同时多取一个 c i c_i ci,结合 引理 1,有 M ( W ) > G ( c i − 1 − 1 ) M(W) > G(c_{i-1} - 1) M(W)>G(ci−1−1)。
结合上面
2
2
2 个,有
M
(
W
−
c
j
)
≤
G
(
c
i
−
1
−
1
)
<
M
(
W
)
M(W - c_j) \leq G(c_{i-1} - 1) < M(W)
M(W−cj)≤G(ci−1−1)<M(W)
从
M
(
W
)
→
M
(
W
−
c
j
)
M(W) \to M(W - c_j)
M(W)→M(W−cj) 只是少取了
1
1
1 次
c
j
c_j
cj,字典序大小却发生了改变,因此
M
(
W
)
M(W)
M(W) 和
G
(
c
i
−
1
−
1
)
G(c_{i-1} - 1)
G(ci−1−1) 的前
j
−
1
j - 1
j−1 位是一样的,不然第
j
j
j 位不可能影响到字典序大小关系。
又因为
j
j
j 的定义(引理 3 中)保证
c
j
+
1
∼
n
=
0
c_{j+1 \sim n} = 0
cj+1∼n=0,要想从
>
>
> 变成
≤
\leq
≤,又只减少了
1
1
1 次,只可能
M
(
W
)
M(W)
M(W) 的第
j
j
j 位比
G
(
c
i
−
1
−
1
)
G(c_{i-1} - 1)
G(ci−1−1) 的第
j
j
j 位大
1
1
1。
我们可以枚举 i , j i, j i,j,为了方便求出 M ( W ) M(W) M(W) 的每一位的值,可以把 G ( c i − 1 − 1 ) G(c_{i-1} - 1) G(ci−1−1) 中 v j + 1 ∼ n v_{j+1 \sim n} vj+1∼n 全部去掉,再在第 j j j 位 + 1 +1 +1,就可以求出 W W W 的最优方案,进而求出每一个可能的反例 W W W,再对所有可能的 W W W 取一个 m i n \mathrm{min} min 即可。
需要注意的是, M ( W ) M(W) M(W) 的第 1 1 1 位一定是零。
- 如果 G 1 ≠ 0 G_1 \neq 0 G1=0,引理 2 得 M 1 = 0 M_1 = 0 M1=0。
- 如果 G 1 = 0 G_1 = 0 G1=0,连贪心都不能取到 c 1 c_1 c1,最优方案也肯定取不到。
所以 i i i 从第 2 2 2 位枚举即可。
枚举
i
,
j
i, j
i,j
O
(
n
2
)
O(n^2)
O(n2),求出
W
W
W 、贪心方案 和 最优方案
O
(
n
)
O(n)
O(n)。
总的时间复杂度是
O
(
n
3
)
O(n^3)
O(n3),而
1
≤
n
≤
400
1 \leq n \leq 400
1≤n≤400,可以
A
C
AC
AC。
代码
#include <cstdio>
int Max(int u, int v) { return (u > v) ? u : v; }
int Min(int u, int v) { return (u < v) ? u : v; }
int rint()
{
int x = 0, fx = 1; char c = getchar();
while (c < '0' || c > '9') { fx ^= (c == '-' ? 1 : 0); c = getchar(); }
while ('0' <= c && c <= '9') { x = (x << 3) + (x << 1) + (c ^ 48); c = getchar(); }
if (!fx) return -x;
return x;
}
const int MAX_n = 400;
int n, res = 2e9;
int v[MAX_n + 5];
int main()
{
n = rint();
for (int i = 1; i <= n; i++)
v[i] = rint();
bool ok = false;
for (int i = 2; i <= n; i++)
{
for (int j = i; j <= n; j++)
{
int sum = 0, dp = 0, now = v[i - 1] - 1; // now = W
for (int k = 1; k <= j; k++)
sum += now / v[k], now %= v[k]; // 求出 G(Ci - 1)
int temp = now = v[i - 1] - 1 - now + v[j]; // 去掉第 j 位后面的, 并在第 j 位上 +1
for (int k = i; k <= j; k++)
dp += now / v[k], now %= v[k]; // 最优方案的货币数 M(W)
sum = 0; now = temp;
for (int k = 1; k <= n; k++)
sum += now / v[k], now %= v[k]; // 贪心需要的货币数 G(W)
if (dp < sum) res = Min(res, temp), ok = true; // 和一个可能的值比最小, 并打上标记
}
}
printf("%d\n", ok ? res : -1);
return 0;
}