• 一类斜率优化的dp(特有性质:只能连续,不能交叉)


    hdu3480

    给定一个有n个数的集合,将这个集合分成m个子集,要求子集的并等于全集
    求花费最小。

    花费为该子集的(最大数-最小数)的平方。

    我们将n个数排序,

    a < b < c < d

    那么不可能a,c一个集合,b,c一个集合

    明显a,b一个集合,c,d一个集合更优

    也就是说某一个数只能和它前面的连续几个数合起来形成一个子集。 正是因为有这个性质才能dp

    dp[i][j]表示第j个数在第i个集合的最小花费

    dp[i][j] = min(dp[i][j],dp[i-1][k]) 1<=k<j

    dp[i-1][k1] + (a[j]-a[k1+1])^2 < dp[i-1][k2]+(a[j]-a[k2+1])^2

    dp[i-1][k1]+a[k1]^2-(dp[i-1][k2]+a[k2]^2) < 2a[j]*(a[k1+1]-a[k2+1])

    这样就能够用斜率优化了,由于是求最小值,所以维护一个下凸包就行了。

     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <stdlib.h>
     4 #include <algorithm>
     5 #include <iostream>
     6 #include <queue>
     7 #include <stack>
     8 #include <vector>
     9 #include <map>
    10 #include <set>
    11 #include <string>
    12 #include <math.h>
    13 using namespace std;
    14 #pragma warning(disable:4996)
    15 #pragma comment(linker, "/STACK:1024000000,1024000000")
    16 typedef int LL;                   
    17 const int INF = 1<<30;
    18 /*
    19 给定一个有n个数的集合,将这个集合分成m个子集,要求子集的并等于全集
    20 求花费最小,  花费为什么每个集合的(最大值-最小值)的平方
    21 
    22 dp[i][j]表示第j个数在第i个集合的最小花费
    23 用滚动数组压缩空间
    24 */
    25 
    26 const int N = 10000 + 10;
    27 const int M = 5000 + 10;
    28 LL a[N];
    29 LL dp[2][N];
    30 int q[N], head, tail;
    31 LL dw(LL a, LL b)
    32 {
    33     return (a - b)*(a - b);
    34 }
    35 LL getUp(int k1, int k2, int c)
    36 {
    37     return dp[c][k1] + a[k1 + 1] * a[k1 + 1] - (dp[c][k2] + a[k2 + 1] * a[k2 + 1]);
    38 }
    39 LL getDown(int k1, int k2)
    40 {
    41     return 2 * (a[k1 + 1] - a[k2 + 1]);
    42 }
    43 int main()
    44 {
    45     int t, n, m;
    46     scanf("%d", &t);
    47     for (int k = 1; k <= t; ++k)
    48     {
    49         scanf("%d%d", &n, &m);
    50         for (int i = 1; i <= n; ++i)
    51             scanf("%d", &a[i]);
    52         sort(a + 1, a + n + 1);
    53         n = unique(a + 1, a + n + 1) - a - 1;
    54         if (m > n)
    55             m = n;
    56         int c = 0;
    57         for (int j = 1; j <= n; ++j)
    58             dp[0][j] = dw(a[j], a[1]);
    59         for (int i = 2; i <= m; ++i)
    60         {
    61             head = tail = 0;
    62             for (int j = i; j <= n; ++j)
    63             {
    64                 while (head +1 < tail && getUp(j - 1, q[tail - 1], c)*getDown(q[tail - 1], q[tail - 2])
    65                     <= getUp(q[tail - 1], q[tail - 2], c)*getDown(j - 1, q[tail - 1]))
    66                     tail--;
    67                 q[tail++] = j - 1;
    68                 while (head + 1 < tail&&getUp(q[head + 1], q[head], c) < a[j] * getDown(q[head+1], q[head]))
    69                     head++;
    70                 dp[c ^ 1][j] = dp[c][q[head]] + dw(a[j], a[q[head] + 1]);
    71             }
    72             c ^= 1;
    73         }
    74         printf("Case %d: %d
    ",k, dp[c][n]);
    75     }
    76     return 0;
    77 }
    View Code

    hdu3405

    n头牛,分成几组,每组至少有k头牛
    要求费用最小,

    费用是每组所有牛的moo,减去该组中最小的moo

    将moo排序

    那么如果是分成两组的话,那么不可能是a,c一组, b,d一组
    a,b,一组比c,d一组肯定更优。
    又遇到了有这种性质的题目, 连续比交叉更优,这样,才能用dp来解决,

    dp[j]表示以j结尾的team的最小花费
    (dp[k1]-sum[k1]+k1*moo[k1+1]) - (dp[k2]-sum[k2]+k2*moo[k2+1]) < i*(moo[k1+1]-moo[k2+1])
    dp[k1]+k1*moo[k1+1] - (dp[k2]+k2*moo[k2+1]) < i * (moo[k1+1]-moo[k2+1])
    所以维护递增的斜率

     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <stdlib.h>
     4 #include <algorithm>
     5 #include <iostream>
     6 #include <queue>
     7 #include <stack>
     8 #include <vector>
     9 #include <map>
    10 #include <set>
    11 #include <string>
    12 #include <math.h>
    13 using namespace std;
    14 #pragma warning(disable:4996)
    15 #pragma comment(linker, "/STACK:1024000000,1024000000")
    16 typedef __int64 LL;                   
    17 const int INF = 1<<30;
    18 /*
    19 n头牛,分成几组,每组至少有k头牛
    20 要求费用最小,
    21 费用是每组所有牛的moo,减去该组中最小的moo
    22 a < b < c < d
    23 那么如果是分成两组的话,那么不可能是a,c一组, b,d一组
    24 a,b,一组比c,d一组肯定更优。
    25 又遇到了有这种性质的题目, 连续比交叉更优,这样,才能用dp来解决,
    26 dp[j]表示以j结尾的team的最小花费
    27 (dp[k1]-sum[k1]+k1*moo[k1+1]) - (dp[k2]-sum[k2]+k2*moo[k2+1]) < i*(moo[k1+1]-moo[k2+1])
    28 dp[k1]+k1*moo[k1+1] - (dp[k2]+k2*moo[k2+1]) < i * (moo[k1+1]-moo[k2+1])
    29 所以维护递增的斜率
    30 
    31 */
    32 const int N = 400000 + 10;
    33 LL moo[N], dp[N], sum[N];
    34 int q[N], head, tail;
    35 
    36 LL getUp(int k1, int k2)
    37 {
    38     return dp[k1] + k1*moo[k1 + 1] - sum[k1] - (dp[k2] + k2*moo[k2 + 1] - sum[k2]);
    39 }
    40 LL getDown(int k1, int k2)
    41 {
    42     return moo[k1 + 1] - moo[k2 + 1];
    43 }
    44 int main()
    45 {
    46     int n, t;
    47     while (scanf("%d%d", &n, &t) != EOF)
    48     {
    49         for (int i = 1; i <= n; ++i)
    50         {
    51             scanf("%I64d", &moo[i]);
    52         }
    53         sort(moo + 1, moo + n + 1);
    54         for (int i = 1; i <= n; ++i)
    55             sum[i] = moo[i] + sum[i - 1];
    56         for (int i = t; i <= n; ++i)
    57             dp[i] = sum[i] - i*moo[1];
    58 
    59         head = tail = 0;
    60         q[tail++] = 0;
    61         q[tail++] = t;
    62         int k = t + 1;
    63         for (int i = 2*t; i <= n; ++i)
    64         {
    65             while (head + 1 < tail&&getUp(q[head + 1], q[head]) < i*getDown(q[head + 1], q[head]))
    66                 head++;
    67             dp[i] = dp[q[head]] + sum[i]-sum[q[head]] - (i-q[head])*moo[q[head] + 1];
    68             while (head + 1 < tail&&getUp(k, q[tail - 1])*getDown(q[tail - 1], q[tail - 2]) <= getUp(q[tail - 1], q[tail - 2])*getDown(k, q[tail - 1]))
    69                 tail--;
    70             q[tail++] = k++;
    71         }
    72         printf("%I64d
    ", dp[n]);
    73     }
    74     return 0;
    75 }
    View Code
  • 相关阅读:
    day10 Java学习(开发工具.Eclipse)
    day9 Java学习(面向对象Package关键字)
    day8 Java学习(面向对象.多态&接口)
    day7 Java学习(面向对象.继承)
    day5 Java学习(面向对象)
    day4 Java学习(数组)
    day3 Java学习(循环结构)
    day2 Java学习(位运算符)
    2019-05-26 java学习日记
    2019-05-25 java学习日记
  • 原文地址:https://www.cnblogs.com/justPassBy/p/4741399.html
Copyright © 2020-2023  润新知