• HDU 2829 Lawrence(四边形优化DP)


    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2829

    题意:首先一条链上有n个数,两两之间相乘的结果求和为这条链的权值(n==1的话答案就是0),然后可以进行最多m次操作,每次可以把一条链的某一条边剪断变成两条链,两条链的权值各自算,这样总的权值和会缩小。 题目让你求m次操作后,最小的权值和为多少。

    思路:经典的四边形优化的题目。

    可以参考 赵爽的《动态规划加速原理之四边形不等式》

    简单的说,就是一点状态方程满足论文里面的两个条件的话,那么他们的决策存在单调性,即w(i, j)的决策k = f(i, j) f()函数满足 f(i-1, j) <= f(i, j) <= f(i,j+1)

    然后我们可以利用上一次循环已经记录下来的f(i-1, j)和f(i,j+1)来限定f(i,j)的枚举范围使得本来需要O(n)枚举的复杂度降到O(1) (带常数吧?)

    回归到这题,我们可以这样定义一个状态dp(i, j)代表前j个,i次操作后的最小值,f(i, j)记录最后一次操作的位置。

    那么dp(i, j)的值可以由min{dp(i-1, k) + w(k+1, j)}得到,而k可以从f(i-1,j)枚举到j-1即可,并记录最优的下标作为f(i,j)的值。

    这样可以做到总体复杂度为O(n^2)左右。

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 
     5 using namespace std;
     6 typedef __int64 lld;
     7 const lld INF = 1LL<<60;
     8 const int MAXN = 1005;
     9 int arr[MAXN], n, m;
    10 int sum[MAXN];
    11 int F[MAXN][MAXN];
    12 lld dp[MAXN][MAXN];
    13 lld w[MAXN][MAXN];
    14 
    15 void init() {
    16     memset(w, 0, sizeof(w));
    17     sum[0] = 0;
    18     for (int i = 1; i <= n; i++) {
    19         sum[i] = sum[i-1] + arr[i];
    20     }
    21     for (int i = 1; i <= n; i++) {
    22         for (int j = i + 1; j <= n; j++) {
    23             w[i][j] = w[i][j-1] + arr[j] * (sum[j-1]-sum[i-1]);
    24         }
    25     }
    26 }
    27 
    28 int main() {
    29     while (scanf("%d%d", &n, &m) == 2) {
    30         if (!n && !m) break;
    31         for (int i = 1; i <= n; i++) {
    32             scanf("%d", &arr[i]);
    33         }
    34         init();
    35         for (int i = 1; i <= n; i++) {
    36             dp[0][i] = w[1][i]; F[0][i] = 1;
    37         }
    38         for (int i = 1; i <= m; i++) {
    39             for (int j = 1; j <= i; j++) dp[i][j] = 0;
    40             for (int j = i + 1; j <= n; j++) {
    41                 int ind = F[i-1][j];
    42                 lld Min = INF;
    43                 for (int k = F[i-1][j]; k < j; k++) {
    44                     if (Min > dp[i-1][k] + w[k+1][j]) {
    45                         Min = dp[i-1][k] + w[k+1][j];
    46                         ind = k;
    47                     }
    48                 }
    49                 F[i][j] = ind; dp[i][j] = Min;
    50             }
    51         }
    52         printf("%I64d\n", dp[m][n]);
    53     }
    54     return 0;
    55 }
    View Code

    PS:四边形不等式其实我并没有深透的理解,对于这道题目如何证明状态是满足四边形不等式的我也暂时不会,希望以后会了再来补齐吧。 :(

    对于本题四边形定理成立的简单证明:

    首先w(i, j)表示第i个到第j个构成一条独立的链所获得的值,

    显然因为所有的值都是[1, 100],所以w(i, j)满足区间包含的单调性,假如{ai, ai', aj, aj'}四个是连续的,

    那么w(i, j) = ai*ai'+ai*aj+ai'*aj ; w(i', j') = ai'*aj + ai'*aj' + aj*aj' ; w(i, j') = ai*ai' + ai*aj + ai*aj' + ai'*aj + ai'*aj' + aj*aj' ; w(i', j) = ai' * aj

    显然w(i, j) + w(i', j') <= w(i, j') + w(i', j)

    所以这道题的状态是满足四边形不等式的。

  • 相关阅读:
    进行代码复审训练
    源代码管理工具调查
    一、实现模块判断传入的身份证号码的正确性
    15100364杨舒雅
    20180320作业2:进行代码复审训练
    20180320作业1:源代码管理工具调查
    软工作业02-PSP与单元测试训练
    软工课后作业01
    hello
    代码复审训练 15100360
  • 原文地址:https://www.cnblogs.com/danceonly/p/3895894.html
Copyright © 2020-2023  润新知