【动态规划】石子合并 (ssl 2863)

版权声明:欢迎借鉴,谢绝抄搬。 https://blog.csdn.net/ssllyf/article/details/84860275

石子合并

Description

在一个操场上一排地摆放着N堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。请设计一个程序,计算出将N堆石子合并成一堆的最小得分。

Input

每组数据第1行为一个正整数N(2<=N<=100),以下N行,每行一个正整数,小于10000,分别表示第i堆石子的个数(1<=i<=N)。

Output

对于每组数据输出一个正整数,即最小得分

Sample Input

7

13

7

8

16

21

4

18

Sample Output

239

说明:

本题之前用两个方法做过,这次用三个方法做


方法一

先枚举前面的数(i),再枚举后面的数(j),最后枚举中间的分割线(c)(三重循环) 用i到c-1之间的数加上c到j的数再加上之前的得分,再将所有的值去最小。(计算一个数到另一个数可以用前缀和搞定)

状态转移方程:

f [ i ] [ j ] = m i n ( f [ i ] [ j ] , f [ i ] [ c 1 ] + f [ c ] [ j ] + s [ j ] s [ i 1 ] ) f[i][j]=min(f[i][j],f[i][c-1]+f[c][j]+s[j]-s[i-1])

f[i][j]表示合成i到j的最小得分

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int n,s[101],f[101][101],x;
int main()
{
	memset(f,127/3,sizeof(f));//因为要去最小的,所以要先赋一个大的值
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	  {
	  	scanf("%d",&x);
	  	s[i]=s[i-1]+x;//前缀和
	  	f[i][i]=0;//清零单个的
	  }
	for (int i=n-1;i>=1;i--)
	  for (int j=i+1;j<=n;j++)
	    for (int c=i+1;c<=j;c++)
	      f[i][j]=min(f[i][j],f[i][c-1]+f[c][j]+s[j]-s[i-1]);
	printf("%d",f[1][n]);
}

方法二

先枚举长度(k),再枚举前面的数(i),最后枚举分割线(c),然后后面的数(j)就可以求出来了:

j = i + k 1 j=i+k-1

之后的步骤和方法一 几乎 完全一样

状态转移方程:

f [ i ] [ j ] = m i n ( f [ i ] [ j ] , f [ i ] [ c 1 ] + f [ c ] [ j ] + s [ j ] s [ i 1 ] ) f[i][j]=min(f[i][j],f[i][c-1]+f[c][j]+s[j]-s[i-1])

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int n,x,j,s[101],f[101][101];
int main()
{
	memset(f,127/3,sizeof(f));
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&x);
		s[i]=s[i-1]+x;//前缀和
		f[i][i]=0;
	}
	for (int k=2;k<=n;k++)//复制长度
	  for (int i=1;i<=n-k+1;i++)
	    {
	    	j=i+k-1;//求出后面的数
	    	for (int c=i+1;c<=j;c++)
	    	  f[i][j]=min(f[i][j],f[i][c-1]+f[c][j]+s[j]-s[i-1]);
	    }
	printf("%d",f[1][n]);
}


方法三

方法与前面的一样,但是f[i][j]表示的是从第i个开始后面的j个数,导致了写法不同

动态转移方程

f [ i ] [ j ] = m i n ( f [ i ] [ j ] , f [ i ] [ c ] + f [ i + c ] [ j c ] + s [ i + j 1 ] s [ i 1 ] ) f[i][j]=min(f[i][j],f[i][c]+f[i+c][j-c]+s[i+j-1]-s[i-1])

i+c是第二段的开头,j-c是的二段的长度

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int n,x,s[101],f[101][101];
int main()
{
	memset(f,127/3,sizeof(f));
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&x);
		s[i]=s[i-1]+x;//前缀和
		f[i][1]=0;
	}
	for (int j=2;j<=n;j++)//枚举长度,但表达不一样
	  for (int i=1;i<=n-j+1;i++)//枚举前面的数
	    for (int c=1;c<j;c++)//枚举分割线
	      f[i][j]=min(f[i][j],f[i][c]+f[i+c][j-c]+s[i+j-1]-s[i-1]);
	printf("%d",f[1][n]);
}

猜你喜欢

转载自blog.csdn.net/ssllyf/article/details/84860275