[洛谷P1880][NOI1995]石子合并

区间DP模板题

区间DP模板Code:

for(int len=2;len<=n;len++)
    {
        for(int i=1;i<=2*n-1;i++)  //区间左端点 
        {
            int j = i + len - 1;  //区间右端点 
            for(int k=i;k<j;k++)  //断点位置 
            {
                f[i][j] = min(f[i][j],f[i][k] + f[k + 1][j] + s[j] - s[i - 1]);
            }
        }
    }

 

题目描述

在一个园形操场的四周摆放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

这个题的数据存储特点有一点代表性:破环成列,把长度为n的环转换为长度为2n-1的列,再进行一次动归。

针对于这个题的n很小,我们就可以用它来代表区间长度,这样O(n ^ 3)也能跑过去了

区间DP的概念就是把一个区间的状态一直分割为它的子区间的状态,一直到这个子区间的状态是显然可求的,最后再将它们综合起来

举个栗子:

f[i][j]中我们可以将[i,j]这一个区间划分为[i,k]和[k + 1,j]这两个区间的总状态再进行一次操作

这个的边界就是[i,k]和[k + 1,j]是显然可求的状态

这个题要求一个最大值和最小值的问题

我们可以显而易见地发现最小值一定小于等于最大值

这样我们可以只建立一个数组先求最小再求最大,节省了两个数组的空间(虽然这不是重点qwq)

Code:

 

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring> 
using namespace std;
int f[300][300];  //节省空间 
int s[300];
int n,x,ans = 55312725;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        s[i] = s[i - 1] + x;
        s[i + n] = s[i];  //把长度开到 2n - 1 
    }
    for(int i=1;i<n;i++)
    s[i + n] += s[n];
    memset(f,10,sizeof(f));  //初始化 
    for(int i=1;i<=2*n-1;i++)
    f[i][i] = 0;
    for(int len=2;len<=n;len++)
    {
        for(int i=1;i<=2*n-1;i++)  //区间左端点 
        {
            int j = i + len - 1;  //区间右端点 
            for(int k=i;k<j;k++)  //断点位置 
            {
                f[i][j] = min(f[i][j],f[i][k] + f[k + 1][j] + s[j] - s[i - 1]);
            }
        }
    }
    for(int i=1;i<=n;i++)
    ans = min(ans,f[i][i + n - 1]);
    printf("%d\n",ans);  //最小值一定比最大值要小,所以无需更新 
    for(int len=2;len<=n;len++)
    {
        for(int i=1;i<=2*n-1;i++)  //区间左端点 
        {
            int j = i + len - 1;  //区间右端点 
            for(int k=i;k<j;k++)  //断点位置 
            {
                f[i][j] = max(f[i][j],f[i][k] + f[k + 1][j] + s[j] - s[i - 1]);
            }
        }
    }
    for(int i=1;i<=n;i++)
    ans = max(ans,f[i][i + n - 1]);
    printf("%d\n",ans);
    return 0; 
}

 

posted @ 2019-04-18 19:13  6954717  阅读(167)  评论(0编辑  收藏  举报