[wikioi]石子归并

http://wikioi.com/problem/1048/

区间型动态规划。参考PPT:http://wenku.baidu.com/view/73c1ded5b9f3f90f76c61bc4.html

首先对于这样的动态规划,我现在的经验是可以有两种思路一是从k到k+1,二是分左右两段,此题就是分左右两段。在PPT中的题目,是一个环(或者圈),最后又有一个优化,就是把n个的环变成2*n个的Array,这样就把复杂度保持在O(n^3)。当然本题是非环,更简单一点。

在PPT的总结里也写到:

该类问题的基本特征是能将问题分解成为两两合并的形式。解决方法是对整个问题设最优值,枚举合并点,将问题分解成为左右两个部分,最后将左右两个部分的最优值进行合并得到原问题的最优值。有点类似分治的解题思想。
设前i到j的最优值,枚举剖分(合并)点,将(i,j)分成左右两区间,分别求左右两边最优值。
状态转移方程的一般形式如下:

F(i,j)=Max{F(i,k)+F(k+1,j)+决策,k为划分点

#include <iostream>

#include <climits>

#include <cstring>

using namespace std;

int w[100+5];

int t[100+5][100+5]; // sum from i to j

int f[100+5][100+5]; // min sum to merge from i to j

int main()

{

    int n;

    cin >> n;

    for (int i = 0; i < n; i++)

    {

        cin >> w[i];

    }

    // pre-process

    for (int i = 0; i < n; i++)

    {

        for (int j = i; j < n; j++)

        {

            if (j == i)

            {

                t[i][j] = w[j];

            }

            else

            {

                t[i][j] = t[i][j-1] + w[j];

            }

        }

    }

    memset(f, 0, sizeof(f));

    for (int len = 2; len <= n; len++)

    {

        for (int i = 0; i+len <= n; i++)

        {

            int j = i+len-1;

            f[i][j] = INT_MAX;

            for (int k = i; k < j; k++)

            {

                if (f[i][k]+f[k+1][j] < f[i][j])

                    f[i][j] = f[i][k]+f[k+1][j];

            }

            f[i][j] = f[i][j] + t[i][j];

        }

    }

    cout << f[0][n-1] << endl;

    return 0;

}

有这么几点:
1.其实这题和Scramble String很像,那题当时用的是备忘录方法,主要是觉得这样的循环难写,现在知道了可以从长度len从小到大循环。因为大的长度都是依赖于小的长度的;
2.我用了二维数组来存从i到j的和,但其实用一维数组就可以,只存从0到i的sum,那么到时候sum[j]-sum[i-1]就是了;

 

你可能感兴趣的:(IO)