[p1880][NOI1995]石子合并

题目描述

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


dalaoの题解:(wsqtql%%%)

大概思路:

这题看起来不太好下手的亚子(dp蒟蒻的苦恼)

看了不少题解总结出的思路:先把所有石子复制一遍,模拟出环,再进行区间DP。而在DP时,直接枚举刀口的位置,区间的起点和长度会浪费很多时间,因为刀口不一定正好落在区间内。所以先枚举长度和起点,再根据已知的区间去枚举刀口的位置可以有效地解决这个问题。

(这题算是个区间DP的板子题,代码就放在这里)

代码:

#include<iostream>
#include<cstdio> 
#include<cmath>
#include<cstring>
using namespace std;
int Fmin[201][201];
int Fmax[201][201];
int sum[1001];
int num[1001];
int n;
void read(int &x)
{
    char c=0;
    x=0;
    while(!isdigit(c))
    c=getchar();
    while(isdigit(c))
    x=x*10+c-'0',c=getchar();
}//快读(只适用于正数) 
int main()
{
    memset(Fmin,0x3f3f3f,sizeof(Fmin));
    read(n);
    for(int i=1;i<=n;i++)
    {
        read(num[i]); 
        num[i+n]=num[i];//前缀和可以快速处理出合并时所需的代价 
        Fmin[i][i]=0;
    }
    for(int i=1;i<=2*n;i++)
    {
        sum[i]=sum[i-1]+num[i];
        Fmin[i+n][i+n]=0;
    } 
    for(int p=1;p<=n;p++)//枚举区间长度 
    {
    for(int i=1;i+p<=2*n;i++)//区间起点 
    {   
        int pos=i+p-1;//终点在这 
        for(int k=i;k<pos;k++)//枚举刀口 
        {
            Fmin[i][pos]=min(Fmin[i][pos],Fmin[i][k]+Fmin[k+1][pos]+sum[pos]-sum[i-1]);
            Fmax[i][pos]=max(Fmax[i][pos],Fmax[i][k]+Fmax[k+1][pos]+sum[pos]-sum[i-1]);
            
        }
    }
    } 
     int Max=-1;
     int Min=0x3f3f3f;
     for(int i=1;i<=2*n;i++)
     {
         Max=max(Max,Fmax[i][i+n-1]);//由于是环状,i+n-1正好是一种合并的方法 
         Min=min(Min,Fmin[i][i+n-1]);
     
     }
     cout<<Min<<endl<<Max;
    return 0;
}

再看道题吧。。

题目描述

Caima王国中有一个奇怪的监狱,这个监狱一共有P个牢房,这些牢房一字排开,第i个紧挨着第i+1个(最后一个除外)。现在正好牢房是满的。

上级下发了一个释放名单,要求每天释放名单上的一个人。这可把看守们吓得不轻,因为看守们知道,现在牢房中的P个人,可以相互之间传话。如果某个人离开了,那么原来和这个人能说上话的人,都会很气愤,导致他们那天会一直大吼大叫,搞得看守很头疼。如果给这些要发火的人吃上肉,他们就会安静点。

输入格式

第一行两个数P和Q,Q表示释放名单上的人数;

第二行Q个数,表示要释放哪些人。

【数据规模】

对于100%的数据1≤P≤1000; 1≤Q≤100;Q≤P;且50%的数据 1≤P≤100;1≤Q≤5

输出格式

仅一行,表示最少要给多少人次送肉吃。

输入输出样例

输入 #1
20 3
3 6 14
输出 #1
35


说明/提示

【样例说明】

先释放14号监狱中的罪犯,要给1到13号监狱和15到20号监狱中的19人送肉吃;再释放6号监狱中的罪犯,要给1到5号监狱和7到13号监狱中的12人送肉吃;最后释放3号监狱中的罪犯,要给1到2号监狱和4到5号监狱中的4人送肉吃。

 
 

 大概思路:

想了大概有一个小时。。怎么都想不出来要怎么写。。

因为每一个状态都有后效性,所以正着想怎么也解不出来。。。

后来看了一下大佬的题解,把要释放的人视作断点,将p个人分成q+1个区间,求合并区间至一个区间所需最小值,即可视为合并石子,突然通悟......不过要注意分成q+1个区间时的一些细节。。

顿悟

顿悟!!!!

(代码就点这里吧)

原文地址:https://www.cnblogs.com/Daz-Os0619/p/11693455.html