[USACO08FEB]Making the Grade G 和 CF714E

12 篇文章 0 订阅
6 篇文章 0 订阅

Making the Grade G

题意:

一个农夫需要修路,每一段路的海拔为 A i A_i Ai,修改后的海拔为 B i B_i Bi,花费为 ∣ A i − B i ∣ |A_i-B_i| AiBi,现在农夫想把 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=k1,假设一定成立
对于 n = k n=k n=k,此时如果 A n ≥ B n − 1 A_n≥B_{n-1} AnBn1,那么显然 B n B_n Bn A n A_n An花费为0,结论成立。
此时如果 A n < B n − 1 A_n<B_{n-1} An<Bn1,那么显然 B n B_n Bn有两种选择
选择一要么是继续沿用 B n − 1 B_{n-1} Bn1
选择二要么是把当前的 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...Bn1都要变成 x x x最小,如果前面这些数改成比 x x x更小的数,花费更小的话,那么在 k = n − 1 k=n-1 k=n1时,就会执行,而不是等到 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| xA1+xA2+.....+xAn价值最小,众所周知,这是绝对值函数取最小值, 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(i1,k)+AiBk) ( 1 ≤ k ≤ j ) (1≤k≤j) (1kj),里面的 f ( i − 1 , k ) f(i-1,k) f(i1,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| AiBi,现在问把序列改成严格单调递增的最小花费为多少?

思路:

根据严格单调递增可知 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+11Bi,所以 B i + 1 − ( i + 1 ) ≥ B i − i B_{i+1}-(i+1)≥B_{i}-i Bi+1(i+1)Bii,所以构造新数组 C i = B i − i C_i=Bi-i Ci=Bii,此时问题转化为把 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
如果你有任何建议或者批评和补充,请留言指出,不胜感激

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值