• B


    B - Lawrence

     HDU - 2829 

    这个题目我觉得很难,难在这个dp方程不会写。

    看了网上的题解,看了很久才理解这个dp转移方程

    dp[i][j] 表示前面1~j 位并且以 j 结尾分成了 i 段的最小权值和

    再定义一个数组 w[a,b] 表示 a到b 的权值和,注意这个不是前缀和,而是题目给的那种权值和

    比如 a 到 b  是4 5 1 2 Its Strategic Value is 4*5 + 4*1 + 4*2 + 5*1 + 5*2 + 1*2 = 49. 

    w[a,b]=49

    然后再定义一个val[i] ==w[1,i] 意思就是前缀权值和 val[i]=val[i-1]+sum[i-1]*a[i]

    因为这个w[a,b]不好直接推出来,但是可以借助前缀来推。

    求w[a+1,b]=val[b]-val[a]-(sum[b]-sum[a])*sum[a]

    然后这个dp方程就可以推出来了

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

    但是这个直接暴力n^3肯定是过不了的,所以需要优化,这个式子是不是和之前推过的斜率优化的式子很像。

    如果在dp[i-1,k]之外还存在k这个值,一般都是可以用斜率优化来优化这个dp的。

    然后就可以推出和之前一样的式子(之前指的是D - Pearls HDU - 1300 斜率dp+二分  斜率dp A - Print Article HDU - 3507

    令 F[h]=dp[i-1][h]-val[h]+sum[h]*sum[h]

    所以G[h,k]=(F[h]-F[k)/(sum[h]-sum[k])<sum[j]

    然后就是一样的推导了

    如果存在 i>j>k G[i,j]>G[j,k]

    1 G[i,j]>Gj,k]>sum[t]  k 比 j 优 ,j 比 i 优

    2 G[i,j]>sum[t]>G[j,k] 那么 j 比 i 优,j 比 k 优

    3 sum[t]>G[i,j]>G[j,k] i 比 j 优 j 比k 优

    如果是 i > j > k G[i,j]<G[j,k]

    1 G[i,j]<G[j,k]<sum[t] j 比 k 优 i 比 j 优

    2. G[i,j]<sum[t]<G[j,k] i 比 j 优 k 比 j 优

    3 sum[t]<G[i,j]<G[j,k]  j 比 i 优 k 比 j 优

    这种情况之下,j 肯定是要被排除的,所以如果从后面插入 i 的时候 前面的 j 如果和它构成的斜率小于这个数和之前的那个数构成的斜率,

    那么这个 j 肯定是不要的。

    因为这个前缀和sum是单调的,所以可以用单调队列优化这个dp。

       

      

    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <algorithm>
    #include <queue>
    #include <vector>
    #include <iostream>
    #include <string>
    #define inf 0x3f3f3f3f
    #define inf64 0x3f3f3f3f3f3f3f3f
    using namespace std;
    const int maxn = 1e5 + 10;
    typedef long long ll;
    ll dp[1100][1100], sum[1100], val[1100], a[1100];
    int que[maxn];
    
    ll up(int i,int j,int k)
    {
        return dp[i - 1][j] - val[j] + sum[j] * sum[j] - (dp[i - 1][k] - val[k] + sum[k] * sum[k]);
    }
    
    ll down(int j,int k)
    {
        return sum[j] - sum[k];
    }
    
    ll DP(int i,int j,int k)
    {
        return dp[i - 1][k] + val[j] - val[k] - (sum[j] - sum[k])*sum[k];
    }
    
    int main()
    {
        int n, m;
        while (scanf("%d%d", &n, &m) && (n + m)) {
            sum[0] = 0;
            val[0] = 0;
            for (int i = 1; i <= n; i++) {
                scanf("%lld", &a[i]);
                sum[i] = sum[i - 1] + a[i];
                val[i] = val[i - 1] + sum[i - 1] * a[i];
            }
            for (int i = 1; i <= n; i++) dp[1][i] = val[i];//注意这个题目的初始化
            for (int i = 2; i <= m + 1; i++) {
                int head = 0, tail = 0;//注意dp[i,j]的定义是以j结尾分成了i块的最小价值
                que[tail++] = i - 1;//因为下一段要分成i块,所以前面最少也占了i-1个数,而后面又有可能全部分成一块,注意这种情况不要漏掉
                for (int j = i; j <= n; j++) {//注意这个j必须要从i开始
                    while (head + 1 < tail&&up(i, que[head + 1], que[head]) <= sum[j] * down(que[head + 1], que[head])) head++;
                    dp[i][j] = DP(i, j, que[head]);
                    while (head + 1 < tail&&up(i, j, que[tail - 1])*down(que[tail - 1], que[tail - 2]) <= up(i, que[tail - 1], que[tail - 2])*down(j, que[tail - 1])) tail--;
                    que[tail++] = j;
                }
            }
            printf("%lld
    ", dp[m + 1][n]);
        }
        return 0;
    }
  • 相关阅读:
    python直接赋值、浅拷贝与深拷贝的区别解析
    join shuffle
    Python工作流-Airflow
    【JAVA基础语法】(一)Arrays.asList的使用
    Java中的数组和List
    ArrayList和LinkedList区别
    Array和ArrayList区别
    iOS项目崩溃日志采集与分析
    iOS超全开源框架、项目和学习资料汇总
    iOS webView、WKWebView、AFNetworking 中的cookie存取
  • 原文地址:https://www.cnblogs.com/EchoZQN/p/11402172.html
Copyright © 2020-2023  润新知