四边形不等式优化石头子儿合并Codevs3002题解

四边形不等式优化石子合并Codevs3002题解
  • 题目描述 Description
    有n堆石子排成一列,每堆石子有一个重量w[i], 每次合并可以合并相邻的两堆石子,一次合并的代价为两堆石子的重量和w[i]+w[i+1]。问安排怎样的合并顺序,能够使得总合并代价达到最小。

  • 输入描述 Input Description
    第一行一个整数n(n3000
    第二行n个整数w1,w2...wn(wi3000)

  • 输出描述 Output Description
    一个整数表示最小合并代价

  • 样例输入 Sample Input
    4
    4 1 1 4

  • 样例输出 Sample Output
    18

  • 题解
    写出来普通的区间dp方程,发现时间复杂度是O(n3)的,不能通过本题。这种题可以使用四边形不等式优化。
    有一篇叫“动态规划加速原理之四边形不等式”的文章对四边形不等式介绍得很好。
    形如本题的状态转移方程都可以用四边形不等式优化,这里不再证明。(实在不会证可以拿优化过的和裸dp的对拍啊,四边形不等式不难写)
    这里介绍一下边界:
    只有一堆石子时,f(i,i)=0f(i,i)的最大值在i处取到,所以f(i,i)对应决策变量最大值为s(i,i)=i
    只要分析好边界的值是自变量等于什么时取到的,自然可以得到决策变量最大值的边界。以后转移时只需在更新f的同时更新s即可。

  • Code

#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 3010, oo = 1000000000;
int f[maxn][maxn], s[maxn][maxn];
int n, a[maxn], w[maxn][maxn];
void init()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
    {
        scanf("%d", &a[i]);
        w[i][i] = a[i];
    }
    for(int i = 1; i <= n; ++i) for(int j = i + 1; j <= n; ++j)
        w[i][j] = w[i][j - 1] + a[j];
}
void work()
{
    for(int i = 1; i <= n; ++i) s[i][i] = i;
    for(int p = 1; p < n; ++p)  for(int i = 1; i <= n - p; ++i)
    {
        int j = i + p;
        f[i][j] = oo;
        for(int k = s[i][j - 1]; k <= s[i + 1][j]; ++k)
            if(f[i][j] > f[i][k] + f[k + 1][j] + w[i][j])
            {
                f[i][j] = f[i][k] + f[k + 1][j] + w[i][j];
                s[i][j] = k;
                //因为这里k是递增的,所以不用取max
            }
    }
    printf("%d", f[1][n]);
}
int main()
{
    init();
    work();
    return 0;
}