环形石子合并问题(动态规划)(洛谷P1880)

                    环形石子合并问题(动态规划)

传统的石子合并问题为:有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动相邻的2堆石子合并,合并花费为新合成的一堆石子的数量,求将这N堆石子合并成一堆的总花费最小(或最大)。

这类问题类似区间DP的解法,设dp[i][j]为合并从i到j的最小总花费,那么预处理出前缀和,转移方程为dp[i][j]=max(dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]),且i==j时dp[i][j]=0;核心代码为:

    for(int v = 1; v < n; v++)
    {
        for(int i = 0;i < n-v; i++)
        {
            int j = i + v;
            dp[i][j] = INF;
            int tmp = sum[j] - (i > 0 ? sum[i-1]:0);
            for(int k = i; k < j; k++)
                dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j] + tmp);
        }
    }

环形石子合并问题为洛谷P1880

题目描述

在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.

输入输出格式

输入格式:

数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.

输出格式:

输出共2行,第1行为最小得分,第2行为最大得分.

输入输出样例

输入样例#1: 
4
4 5 9 4
输出样例#1: 
43
54





题解:对于环形的处理典型的方法是将环拆成链,也就是将长度扩大2倍,求链的最佳得分可以通过反向枚举左端点,然后找到当前到右端点的最佳,因为链的长度为原来的两倍,所以应该从2*n-1枚举,每次枚举n的长度,然后从1-n找最后的答案即可。

AC代码:

#include <iostream>
#include <algorithm>
using namespace std;
#define _for(i,a,b) for(int i=a;i<=b;i++)
const int maxn = 507;
const int inf = 0x3f3f3f;
int n,m,a[maxn],sum[maxn],dp_max[maxn][maxn],dp_min[maxn][maxn];
int main(int argc, char const *argv[])
{
    cin>>n;
    _for(i,1,n)
    {
        cin>>a[i];
        sum[i]=sum[i-1]+a[i];
    }
    _for(i,n+1,n*2)
    {
        a[i]=a[i-n];
        sum[i]=sum[i-1]+a[i];
    }
    for(int i=n*2-1;i>0;i--)
    {
        _for(j,i+1,i+n)
        {   
            dp_min[i][j]=inf;
            _for(k,i,j-1)
            {
                dp_min[i][j]=min(dp_min[i][j],dp_min[i][k]+dp_min[k+1][j]+sum[j]-sum[i-1]);
                dp_max[i][j]=max(dp_max[i][j],dp_max[i][k]+dp_max[k+1][j]+sum[j]-sum[i-1]);
            }
        }
    }
    int ans_min = inf;
    int ans_max = 0;
    _for(i,1,n)
    {
        ans_max=max(dp_max[i][i+n-1],ans_max);
        ans_min=min(dp_min[i][i+n-1],ans_min);
    }
    cout<<ans_min<<endl;
    cout<<ans_max<<endl;
    return 0;
}

阅读更多

没有更多推荐了,返回首页