UVA - 12594 Naming Babies(斜率优化)

来源:互联网 发布:勤思考研网络课程 编辑:程序博客网 时间:2024/06/14 06:51

题目大意:给出一个字符串,要求你将这个字符串分成k段
每段的代价为sigma(i - pos) * pos
求所有段的最小代价和

解题思路:用dp[i][j]表示前i个字符分成j段的最小代价和
得到转移方程dp[i][j] = dp[k][j - 1] + (0 * pos[k + 1] + 1 * pos[k + 2] + … + (i - k - 1) * pos[i]) - (pos[k + 1] ^ 2 + pos[k + 2] ^ 2 + … + pos[i] ^ 2)
=dp[k][j - 1] + (k * pos[k + 1] + (k + 1) * pos[k + 2] + … + (i - 1) * pos[i] ) -k * (pos[k + 1] + pos[k + 2] + … + pos[i]) - (pos[k + 1] ^ 2 + pos[k + 2] ^ 2 + … + pos[i] ^ 2)

很长的一个式子,接下来的问题就是处理括号里的那些数据了
用s1表示 (pos[k + 1] + pos[k + 2] + … + pos[i])的前缀和
用s2表示 (k * pos[k + 1] + (k + 1) * pos[k + 2] + … + (i - 1) * pos[i] )的前缀和
用s3表示(pos[k + 1] ^ 2 + pos[k + 2] ^ 2 +… + pos[i] ^ 2)的前缀和
则dp方程表示如下
dp[i][j] = dp[k][j - 1] + (s2[i] - s2[k]) - k * (s1[i] - s1[k]) - (s3[i] - s3[k])
设l > k,且点l比点k优
则dp[k][j - 1] + (s2[i] - s2[k]) - k * (s1[i] - s1[k]) - (s3[i] - s3[k]) >= dp[l][j - 1] + (s2[i] - s2[l]) - l * (s1[i] - s1[l]) - (s3[i] - s3[l])
化简得s1[i] >= ( (dp[l][j - 1] - s2[l] + s3[l] + l * s2[l]) - (dp[k][j - 1] - s2[k] + s3[k] + k * s2[k]) ) / (l - k)
因为s1随着i的递增而递增,所以得到斜率方程

#include <cstdio>#include <cstring>const int N = 20010;const int M = 30;typedef long long LL;char str[M], name[N];int n, m;int que[N], pos[M];LL s1[N], s2[N], s3[N], val[N], dp[510][N];void init() {    scanf("%s%d", str, &m);    int len = strlen(str);    for (int i = 0; i < len; i++)        pos[str[i] - 'a'] = i;    scanf("%s", name);    n = strlen(name);    val[1] = pos[name[0] - 'a'];    s1[1] = val[1]; s2[1] = 0; s3[1] = val[1] * val[1];    for (int i = 1; i < n; i++) {        val[i + 1] = pos[name[i] - 'a'];        s1[i + 1] = s1[i] + val[i + 1];        s2[i + 1] = s2[i] + i * val[i + 1];        s3[i + 1] = s3[i] + val[i + 1] * val[i + 1];    }}LL getUp(int l, int k, int i) {    return (dp[i - 1][l] - s2[l] + s3[l] + l * s1[l]) - (dp[i - 1][k] - s2[k] + s3[k] + k * s1[k]);}LL getDown(int l, int k) {    return l - k;}void getDp(int i, int j, int k) {    dp[i][j] = dp[i - 1][k] + s2[j] - s2[k] - k * (s1[j] - s1[k]) - (s3[j] - s3[k]);}int cas = 1;void solve() {    int head, tail;    for (int i = 1; i <= n; i++) dp[1][i] = s2[i] - s3[i];    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) <= s1[j] * getDown(que[head + 1], que[head])) head++;            getDp(i, j, que[head]);            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;        }    }    printf("Case %d: %lld\n", cas++, dp[m][n]);}int main() {    int test;    scanf("%d", &test);    while (test--) {        init();        solve();    }    return 0;}
1 0
原创粉丝点击