HDU - 3480 Division(斜率优化)

题目大意:给出N个数,要求你将N个数分成K个集合,使每个集合的(最大值 - 最小值)^ 2和达到最小

解题思路:先排个序,从小打大排
设dp[i][j]为前i个数分成j个集合最小平方和
得到转移方程dp[i][j] = dp[k][j - 1] + (val[i] - val[k + 1]) ^ 2
val[i]为第i个数的值
设l > k,且点l比点k优
则dp[k][j - 1] + (val[i] - val[k + 1]) ^ 2 >= dp[l][j - 1] + (val[i] - val[l + 1]) ^ 2
化简得val[i] >= (dp[l][j - 1] + val[l+ 1] ^ 2 - dp[k][j - 1] - val[k + 1] ^ 2 ) / (2 * val[l + 1] - 2 * val[k + 1])
因为val随着i的增大而增大,所以得到斜率方程

#include 
#include 
#include 
using namespace std;
typedef long long LL;
const int N = 10010;
const int M = 5010;

LL val[N];
LL dp[M][N];
int n, m;
int que[N];

void init() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &val[i]);
    sort(val + 1, val + 1 + n);
}

LL getUp(int l, int k, int j) {
    return dp[j - 1][l] + val[l + 1] * val[l + 1] - (dp[j - 1][k] + val[k + 1] * val[k + 1]);
}

LL getDown(int l, int k) {
    return 2 * (val[l + 1] - val[k + 1]);
}

void getDp(int i, int k, int j) {
    dp[j][i] = dp[j - 1][k] + (val[i] - val[k + 1]) * (val[i] - val[k + 1]);
}

int cas = 1;
void solve() {
    for (int i = 1; i <= n; i++)
        dp[1][i] = (val[i] - val[1]) * (val[i] - val[1]);
    int head, tail;
    for (int i = 2; i <= m; i++) {
        head = tail = 0;
        que[tail++] = i - 1;
        for (int j = i; j <= n; j++) {
            while (head + 1 < tail && getUp(que[head + 1], que[head], i) <= getDown(que[head + 1], que[head]) * val[j]) head++;
            getDp(j, que[head], i);
            while (head + 1 < tail && getUp(j, que[tail - 1], i) * getDown(que[tail - 1], que[tail - 2]) <= getUp(que[tail - 1], que[tail - 2], i) * getDown(j, que[tail - 1])) tail--;
            que[tail++] = j;
        }
    }
    if (m >= n) dp[m][n] = 0;
    printf("Case %d: %lld\n", cas++, dp[m][n]);

}

int main() {
    int test;
    scanf("%d", &test);
    while (test--) {
        init();
        solve();
    }
    return 0;
}

你可能感兴趣的:(ACM-动态规划-斜率DP)