石子合并
现在有n块石头,多多要把这n个石头进行合并
每一次合并,多多可以把相邻两个石子合并到一起,得分等于两个石头的重量之和。
可以看出,所有的石子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共的得分等于每次合并石头质量之和。
求得分最少是多少,最多是多少? (n<=100)
样例输入#1
4
4 4 5 9
样例输出#1
43
54
样例解释#1
得分最大情况:
第一步,多多先把5和9合并,得分为14,各石子数变成:4 4 14
第二步,多多先把14和4合并,得分为18,各石子变成:4 18
第三步,多多不得不把4和18合并,得分为22,,只剩下一堆石子:22
各得分和为54,可以证明分数最大为54;
得分最小情况:
第一步,多多先把4和4合并,得分为8,各石子变成:8 5 9
第二步,多多先把8和5合并,得分为13,各石子变成:13 9
第三步,多多不得不把13和9合并,得分为22,,只剩下一堆石子:22
各得分和为43,可以证明分数最小为43;
【题解】
可以知道,这道题是经典的区间dp问题;我们设dp[i][j]为i~j中操作的最大得分(以最大为例)
这里的sum[i,j]表示原数列中的i+i+1+.......+j的值,我们可以通过O(n^2)的预处理来求出sum
第0阶段:dp[1][1],dp[2][2],dp[3][3],dp[4][4] 因为一开始还没有合并,所以这些值应该全部为0。
第1阶段:两两合并过程如下,其中sum(i,j)表示石头的数量,即从i开始数j个数的和
dp[1,2]=dp[1,1]+dp[2,2]+sum[1,2];
dp[2,3]=dp[2,2]+dp[3,3]+sum[2,3];
dp[3,4]=dp[3,3]+dp[4,4]+sum[4,4];
第2阶段:三三合并可以拆成两两合并,拆分方法有两种,前两个为一组或后两个为一组
dp[1,3]=dp[1,2]+dp[3,3]+sum[1,3]或dp[1,3]=dp[1,1]+dp[2,3]+sum[1,3];取其最优
dp[2,4]=dp[2,2]+dp[3,4]+sun[2,4]或dp[2,4]=dp[2,3]+dp[3,3]+sum[2,4];取其最优
第3阶段,四四合并可以拆成三三合并,拆分方法有一组,
dp[1,4]=dp[1,1]+dp[2,4]+sum[1,4]或dp[1,2]+dp[3,4]+sum[1,4]或dp[1,3]+dp[4,4]+sum[1,4]取其最优;
于是,简单的距离后我们发现一个事实:每一个dp式都是由三个部分构成的(i≠j时)
dp[i,j]:dp[i,k]+dp[k+1,j]+sum[i,j](i,j需要枚举)
从而得出下面的转移方程:
dp[i,j]:=max(dp[i,j],dp[i,k]+dp[k+1,j]+sum[i,j]); | i<=k<j |
答案就是dp[1,n]
代码实现比较简单:
石子合并(最大得分):
uses math; var n,i,j,len,k,a,xx,yy:longint; sum,dp:array[0..1000,0..1000]of longint; s:array[0..1000]of longint; begin readln(n); for i:=1 to n do begin read(a); s[i]:=s[i-1]+a; end; for i:=1 to n do for j:=i to n do sum[i,j]:=s[j]-s[i-1]; for i:=1 to n do dp[i,i]:=0; for len:=1 to n do begin i:=0; j:=0; while (i<=n)and(j<=n) do begin inc(i); j:=len+i; for k:=i to j-1 do dp[i,j]:=max(dp[i,j],dp[i,k]+dp[k+1,j]+sum[i,j]); end; end; writeln(dp[1,n]); end.
石子合并最小得分:
//注意赋初值!! uses math; var n,i,j,len,k,a,xx,yy:longint; sum,dp:array[0..1000,0..1000]of longint; s:array[0..1000]of longint; begin readln(n); for i:=1 to n do begin read(a); s[i]:=s[i-1]+a; end; for i:=1 to n do for j:=i to n do sum[i,j]:=s[j]-s[i-1]; for i:=1 to n do for j:=1 to n do dp[i,j]:=maxint; for i:=1 to n do dp[i,i]:=0; for len:=1 to n do begin i:=0; j:=0; while (i<=n)and(j<=n) do begin inc(i); j:=len+i; for k:=i to j-1 do dp[i,j]:=min(dp[i,j],dp[i,k]+dp[k+1,j]+sum[i,j]); end; end; writeln(dp[1,n]); end.