Codechef July Challenge 2019 Hit the Coconuts

 

假设现在有一堆数,我想要保证能取出一个,至少需要敲 (数的个数)*(这些数里的最小值)
那么把这些数从大到小排序,$dp[i][j]$ 表示前 $i$ 个里面保证能取出 $j$ 个需要敲的次数。
$dp[i][k] = min(dp[j][k - 1] + (i - j) \times a[i])$
斜率优化解决。这里的 $a[i]$ 是单调递减的。即斜率是单调递减的。
那么下凸壳维护的最优决策点是越来越靠左的,所以pop的是右端,即是一个单调栈。

#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int N = 1e3 + 7;
const double eps = 1e-10;
int n, z;
ll a[N], dp[N][N];
int que[N], l, r;

inline ll X(int i) { return i; }
inline ll Y(int i, int cur) { return dp[i][cur]; }
inline double K(int i, int j, int cur) { return (Y(i, cur) - Y(j, cur)) / (X(i) - X(j)); }
inline int dcmp(double x) {
    if (fabs(x) < eps) return 0;
    return x < 0 ? -1 : 1;
}

int main() {
    freopen("in.txt", "r", stdin);
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &n, &z);
        for (int i = 1; i <= n; i++)
            scanf("%lld", &a[i]);
        sort(a + 1, a + 1 + n, greater<ll>());
        for (int i = 1; i <= n; i++)
            dp[i][1] = i * a[i];
        for (int k = 2; k <= z; k++) {
            que[l = 1] = 0;
            r = 0;
            que[++r] = k - 1;
            for (int i = k; i <= n; i++) {
                while (l < r && dcmp(K(que[r], que[r - 1], k - 1) - a[i]) >= 0) r--;
                int j = que[r];
                dp[i][k] = dp[j][k - 1] + (i - j) * a[i];
                while (l < r && dcmp(K(que[r], que[r - 1], k - 1) - K(que[r], i, k - 1)) >= 0) r--;
                que[++r] = i;
            }
        }
        ll ans = 1e18;
        for (int i = z; i <= n; i++)
            ans = min(ans, dp[i][z]);
        printf("%lld\n", ans);
    }
    return 0;
}
View Code

 

posted @ 2019-10-14 21:55  Mrzdtz220  阅读(209)  评论(0编辑  收藏  举报