题目描述
在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.
输入格式
数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.
输出格式
输出共2行,第1行为最小得分,第2行为最大得分.
输入输出样例
4 4 5 9 4
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
输出格式
仅一行,表示最少要给多少人次送肉吃。
输入输出样例
20 3 3 6 14
35
说明/提示
【样例说明】
先释放14号监狱中的罪犯,要给1到13号监狱和15到20号监狱中的19人送肉吃;再释放6号监狱中的罪犯,要给1到5号监狱和7到13号监狱中的12人送肉吃;最后释放3号监狱中的罪犯,要给1到2号监狱和4到5号监狱中的4人送肉吃。
大概思路:
想了大概有一个小时。。怎么都想不出来要怎么写。。
因为每一个状态都有后效性,所以正着想怎么也解不出来。。。
后来看了一下大佬的题解,把要释放的人视作断点,将p个人分成q+1个区间,求合并区间至一个区间所需最小值,即可视为合并石子,突然通悟......不过要注意分成q+1个区间时的一些细节。。
顿悟
顿悟!!!!