Making the Grade G
题意:
一个农夫需要修路,每一段路的海拔为 A i A_i Ai,修改后的海拔为 B i B_i Bi,花费为 ∣ A i − B i ∣ |A_i-B_i| ∣Ai−Bi∣,现在农夫想把 A 1 , A 2 . . . . . A n A_1,A_2.....A_n A1,A2.....An的海拔改成单调的,问最少花费多少?
思路:
此题需要先推导一个结论,就是修改完后的序列里的每个数
B
i
B_i
Bi一定是原序列
A
A
A中已经存在过的。
以把序列改成非严格单调递增的为例,来证明。
改成非严格单调递减序列也是同样的证法。
证明:(数学归纳法)
n
=
1
n=1
n=1,一定成立
对于
n
=
k
−
1
n=k-1
n=k−1,假设一定成立
对于
n
=
k
n=k
n=k,此时如果
A
n
≥
B
n
−
1
A_n≥B_{n-1}
An≥Bn−1,那么显然
B
n
B_n
Bn用
A
n
A_n
An花费为0,结论成立。
此时如果
A
n
<
B
n
−
1
A_n<B_{n-1}
An<Bn−1,那么显然
B
n
B_n
Bn有两种选择
选择一要么是继续沿用
B
n
−
1
B_{n-1}
Bn−1
选择二要么是把当前的
B
n
B_n
Bn下调为
x
x
x,并且前面构造好的
B
i
B_i
Bi都要下调,把
B
1
,
B
2
.
.
.
B
n
−
1
B_1,B_2...B_{n-1}
B1,B2...Bn−1都要变成
x
x
x最小,如果前面这些数改成比
x
x
x更小的数,花费更小的话,那么在
k
=
n
−
1
k=n-1
k=n−1时,就会执行,而不是等到
k
=
n
k=n
k=n时,所以此时就是选择一个
x
x
x使得
∣
x
−
A
1
∣
+
∣
x
−
A
2
∣
+
.
.
.
.
.
+
∣
x
−
A
n
∣
|x-A_1|+|x-A_2|+.....+|x-A_n|
∣x−A1∣+∣x−A2∣+.....+∣x−An∣价值最小,众所周知,这是绝对值函数取最小值,
x
x
x取这个序列的中位数即可,所以结论成立。
在知道结论的情况下,就可以定义状态方程
f
(
i
,
j
)
f(i,j)
f(i,j)序列
A
1
.
.
.
.
A
i
A_1....A_i
A1....Ai为止,第
i
i
i个数
B
i
B_i
Bi选择的时升序(降序)排序后序列里的第
j
j
j个数
所以此时
f
(
i
,
j
)
=
m
i
n
(
f
(
i
−
1
,
k
)
+
∣
A
i
−
B
k
∣
)
f(i,j)=min(f(i-1,k)+|A_i-B_k|)
f(i,j)=min(f(i−1,k)+∣Ai−Bk∣)
(
1
≤
k
≤
j
)
(1≤k≤j)
(1≤k≤j),里面的
f
(
i
−
1
,
k
)
f(i-1,k)
f(i−1,k)可以通过在算
f
(
i
,
j
)
f(i,j)
f(i,j)的时候,也同时取出来,所以只需时间复杂度
O
(
N
2
)
O(N^2)
O(N2)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2222;
int f[N][N];
int n, a[N];
int b[N];
int c[N];
signed main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
b[i] = a[i];
}
int cnt = 0;
sort(b + 1, b + n + 1);
for (int i = 1; i <= n; i++) {
if (i == 1 || b[i] != b[i - 1]) {
++cnt;
}
c[cnt] = b[i];
}
for (int i = 1; i <= n; i++) {
int minv = f[i - 1][1];
for (int j = 1; j <= cnt; j++) {
minv = min(minv, f[i - 1][j]);
f[i][j] = minv + abs(c[j] - a[i]);
}
}
int res = f[n][1];
for (int i = 2; i <= cnt; i++) {
res = min(res, f[n][i]);
}
reverse(a + 1, a + 1 + n);
for (int i = 1; i <= n; i++) {
int minv = f[i - 1][1];
for (int j = 1; j <= cnt; j++) {
minv = min(minv, f[i - 1][j]);
f[i][j] = minv + abs(c[j] - a[i]);
}
}
for (int i = 1; i <= cnt; i++) {
res = min(res, f[n][i]);
}
cout << res << endl;
return 0;
}
E. Sonya and Problem Wihtout a Legend
题意:
给定一个长度为 N N N的 A A A序列,现在要把 A A A序列改成严格单调递增的序列 B B B,每次修改的花费为 ∣ A i − B i ∣ |A_i-B_i| ∣Ai−Bi∣,现在问把序列改成严格单调递增的最小花费为多少?
思路:
根据严格单调递增可知 B i + 1 > B i B_{i+1}>B_{i} Bi+1>Bi,所以 B i + 1 − 1 ≥ B i B_{i+1}-1≥B_{i} Bi+1−1≥Bi,所以 B i + 1 − ( i + 1 ) ≥ B i − i B_{i+1}-(i+1)≥B_{i}-i Bi+1−(i+1)≥Bi−i,所以构造新数组 C i = B i − i C_i=Bi-i Ci=Bi−i,此时问题转化为把 C C C数组改为非严格单调递增的序列的最小花费?此时就是上面那个问题了。
#include <bits/stdc++.h>
using namespace std;
const int N = 3333;
#define ll long long
int n, m;
int a[N];
ll f[N][N];
int b[N];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
a[i] -= i;
b[i] = a[i];
}
sort(a + 1, a + n + 1);
m = unique(a + 1, a + n + 1) - (a + 1);
for (int i = 1; i <= n; i++) {
ll minv = f[i - 1][1];
for (int j = 1; j <= m; j++) {
minv = min(f[i - 1][j], minv);
f[i][j] = minv + abs(b[i] - a[j]);
}
}
ll res = 1e18;
for (int i = 1; i <= m; i++) {
res = min(res, f[n][i]);
}
cout << res << endl;
}
To be continued
如果你有任何建议或者批评和补充,请留言指出,不胜感激