[线性动态规划][区间类动态规划问题][ NOI1995 石子合并]环状区间DP基本解法

题目链接:P1880 [NOI1995]石子合并

题目描述

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

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

输入输出格式

输入格式:

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

输出格式:

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

思路:

本题要求了相邻两堆石子的合并,那么本题就是一道经典的区间类动态规划问题,

该题目的动态规划特征:求一个大区间的最优解,而大区间是由小区间合并而来,所以只要小区间为最优解,那么合并求解得到的大区间也必然为最优解。 

那么可以得到动态规划方程(方程中的变量的意义可以参考代码理解):

 dp[i][j]=0,i=j

dp[i][j] = min\{dp[i][j],dp[i][k]+dp[k+1][j]+sum[i,j]\}

表示区间[i,j]的最小合并代价,k表示区间[i,j]内的分割点,用于求dp[i][j]的最优解.

其中因为合并石子的时候,代价 = 两个区间各自的代价 + 这两个区间石子的总量(也就是合并后,i到j的石子的总量) 

当i==j的时候,不存在合并代价,赋值为零.

求最大合并代价只需改变min为max.

上述思路可以直接求解线状石子合并类问题。

 

本题中由于石堆摆在了一个环形操场,那么是一个环状区间DP,采用的解法是破环为链,将环复制一份存到数组,进行DP,最后求出一个区间[i,j](长度为N)有最优解即可。

AC代码:

#include 
#include 

using namespace std;
int Sum[201];
int Max[201][201] = {0};
int Min[201][201] = {0};
int Stone[201];
int N;

int main(void)
{
    scanf("%d",&N);
    Sum[0] = 0;
    for(int i = 1;i <= N;i++)
    {
        scanf("%d",&Stone[i]);
        Sum[i] = Stone[i]+Sum[i-1];
    }
    for(int i = 1;i <= N;i++)
    {
        Stone[i+N] = Stone[i];
        Sum[i+N] = Sum[i+N-1]+Stone[i+N];
    }
    
    for(int p = 1;p < N;p++)
    {
        for(int i =1;i+p <= 2*N;i++)
        {
            int r = i+p;	//分割点上界 
            for(int j = i;j < r;j++)	//DP分割点 
            {
                Max[i][r] = max(Max[i][r],Max[i][j]+Max[j+1][r] + Sum[r]-Sum[i-1]); 
            }
        }
    }
    
    for(int p = 1;p < N;p++)
    {
        for(int i =1;i+p <= 2*N;i++)
        {
            int r = i+p;	//分割点上界 
            Min[i][r] = 12180;
            for(int j = i;j < r;j++)	//DP分割点 
            {
                Min[i][r] = min(Min[i][r],Min[i][j]+Min[j+1][r] + Sum[r]-Sum[i-1]); 
            }
        }
    }
    
    int ans1 = 12180;
    int ans2 = 0;
    for(int i = 1;i <= N;i++)
    {
        ans2 = max(ans2,Max[i][i+N-1]);
    }
    
    for(int i = 1;i <= N;i++)
    {
        ans1 = min(ans1,Min[i][i+N-1]);
    }
    
    printf("%d\n",ans1);
    printf("%d",ans2);
    return 0;
}

 

区间DP套路

for SectonLength ... n
    for StartPoint ... EndPoint
        for BreakPoint in [StartPoint,EndPoint]
            DP();

 

你可能感兴趣的:(算法,NOIP,C++)