HDU - 2829 Lawrence(斜率优化)

题目大意:给你N个数,要求你分成M个部分,每部分的得分为,假设该部分为i, j, k, 那么的分就为i * j + i * k + j * k
问划分后,至少能得多少分

解题思路:设dp[i][j]为前面i个数,分成j个部分能得到得最小得分
得转移方程dp[i][j] = dp[k][j - 1] + cost[i] - cost[k] - (sum[i] - sum[k]) * sum[k]
这里的cost[i]表示的是前i个数为1个部分的情况下所能得到的分
因为cost[i] - cost[k]后,还多了sum[k] * (sum[i] - sum[k]),所以后面要减去
现在设l > k,且在l比j更优
则dp[k][j - 1] + cost[i] - cost[k] - sum[k] * (sum[i] - sum[k]) >= dp[l][j - 1] + cost[i] - cost[l] - sum[l] * (sum[i] - sum[l])
化简可得
sum[i] >= (dp[l][j - 1] - cost[l] + sum[l] ^ 2 - dp[k][j - 1] + cost[k] - sum[k] ^ 2) / (sum[l] - sum[k])

这样就得到斜率优化的式子了

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;

int n, m;
int sum[N], cost[N], que[N], dp[N][N];

int getUp(int j, int k, int boom) {
    return dp[j][boom] - cost[j] + sum[j] * sum[j] - (dp[k][boom] - cost[k] + sum[k] * sum[k]);
}

int getDown(int j, int k) {
    return sum[j] - sum[k];
}

void solve() {
    for (int i = 1; i <= n; i++) {
        dp[i][0] = cost[i];
        dp[i][i - 1] = 0;
    }

    int head, tail;
    for (int boom = 1; boom <= m; boom++) {
        head = tail = 0;
        que[tail++] = boom;
        for (int i = boom + 1; i <= n; i++) {
            while (head + 1 < tail && getUp(que[head + 1], que[head], boom - 1) <= getDown(que[head + 1], que[head]) * sum[i])
                head++;
            int t = que[head];
            dp[i][boom] = dp[t][boom - 1] + cost[i] - cost[t] + sum[t] * sum[t] - sum[i] * sum[t];

            while (head + 1 < tail && getUp(i, que[tail - 1], boom - 1) * getDown(que[tail - 1], que[tail - 2]) <= getUp(que[tail - 1], que[tail - 2], boom - 1) * getDown(i, que[tail - 1]))
                tail--;
            que[tail++] = i;
        }
    }
    printf("%d\n", dp[n][m]);
}

void init() {
    sum[0] = cost[0] = 0;
    for (int i = 1; i <= n; i++) {
        scanf("%d", &sum[i]);
        cost[i] = cost[i - 1] + sum[i - 1] * sum[i];
        sum[i] += sum[i - 1];
    }
}

int main() {
    while (scanf("%d%d", &n, &m) != EOF && n + m) {
        init();
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值