HDOJ 2829 Lawrence(二维DP + 斜率优化)

题意:

有 n 个点,每个点有路与两侧点相连,要去掉一些路,使整个系统值最小。

思路:

1. 有 m 个炸药点,则可以转化为将线段分成 m + 1 段的情况来求解,dp[m][n] 表示到第 n 点为止,将 1~n 分成 m 段的最小值;

2. dp[k][i] = dp[k-1][j] + cost[j+1, i]; 如果枚举的话时间复杂度为 O(N2 * M); 回到斜率优化,将代码优化到 O(N * M);

3. dp[k][i] = dp[k-1][j] + T[j] - S[i] * S[j] + S[i] * S[i] - T[i]; -> Y = dp[k-1][j] + T[j], X = S[j], a = S[i](递增);

4. 利用下凸函数的特性 + 队列对求 dp[k][i] 进行优化。本题还要克服的一个问题是关于初始化:dp[0][0] = 0, dp[0][else] = INFS;

#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN = 1010;
const int INFS = 0x3fffffff;

int t1, t2, dp[2][MAXN], V[MAXN], S[MAXN], T[MAXN], deq[MAXN];

inline double slope(int i, int j)
{
    return 1.0 * (dp[t1][i] + T[i] - dp[t1][j] - T[j]) / (S[i] - S[j]);
}

int main()
{
    int n, m;
    while (scanf("%d %d", &n, &m) && n && m)
    {
        V[0] = S[0] = T[0] = 0;
        for (int i = 1; i <= n; ++i)
        {
            scanf("%d", &V[i]);
            S[i] = S[i-1] + V[i];
            T[i] = T[i-1] + S[i] * V[i];
        }

        dp[0][0] = 0;
        for (int i = 1; i <= n; ++i)
            dp[0][i] = INFS;

        t1 = 1, t2 = 0;
        for (int k = 1; k <= m + 1; ++k)
        {
            t1 ^= 1, t2 ^= 1;

            int s = 0, e = -1;
            for (int i = 0; i <= n; ++i)
            {
                while (s < e && slope(deq[s], deq[s+1]) <= S[i])
                    ++s;

                while (s < e && slope(deq[e], deq[e-1]) >= slope(i, deq[e]))
                    --e;
                deq[++e] = i;

                int j = deq[s];
                dp[t2][i] = dp[t1][j] + S[i] * (S[i] - S[j]) + T[j] - T[i];             
            }
        }
        
        printf("%d\n", dp[t2][n]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/kedebug/p/2941641.html