【区间DP】合并石子

-题目大意-

有排成一排的n堆石子,要将它们移动成一堆,每次只能移动相邻的两堆石子,且新的石子堆的石子数量为得到的分值,求将它们全部合并后能得到的最小分值。

-Input

第一行n
接下来一行a1,a2…an

-Ouput

最小分值


-方法-

DP,s为前缀和
PS:代码f写不习惯,习惯用a数组。

方法1

枚举边界,然后枚举长度
状态转移方程:
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
i为边界起始点,从n-1到1;
j为边界结束点,从i+1到n;
k为分界(位置),从i后到j-1后分,俩块区域的最优解合并。

#include<cstdio>
int min(int a,int b){
	if(a<b) return a;
	return b;
}
int main(){
	int n,a[101][101]={0},s[101]={0};
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		int aa;
		scanf("%d",&aa);
		s[i]=s[i-1]+aa;
	}
	for(int i=n-1;i>=1;--i)
		for(int j=i+1;j<=n;++j){
		    a[i][j]=a[i][i]+a[i+1][j]+s[j]-s[i-1];
		    for(int k=i+1;k<j;++k)
			  a[i][j]=min(a[i][j],a[i][k]+a[k+1][j]+s[j]-s[i-1]);
		} 
	printf("%d",a[1][n]);
}

方法2

先枚举长度,再枚举边界
状态转移方程:
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1])
len为长度,从2到n;
i为边界起始点,从1到n-j+1(防止超界);
算出j,j为结束点,从i后到j-1后分,俩块区域的最优解合并。
k是分界(位置),从i后到j-1后分,俩块区域的最优解合并。

#include<cstdio>
int min(int a,int b){
	if(a<b) return a;
	return b;
}
int main(){
	int n,a[101][101]={0},s[101]={0};
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		int aa;
		scanf("%d",&aa);
		s[i]=s[i-1]+aa;
	}
	for(int l=2;l<=n;++l)
         for(int i=1;i<=n-l+1;++i)
         {
             int j=i+l-1;
             a[i][j]=10000000;
             for(int k=i;k<=j-1;++k)
                a[i][j]=min(a[i][j],a[i][k]+a[k+1][j]+s[j]-s[i-1]);
         }
	printf("%d",a[1][n]);
}

方法3

枚举长度,然后枚举边界
状态转移方程:
f[i][j]=min(f[i][k]+f[i+k][j-k]+s[i+j-1]-s[i-1],f[i][j]);
j为长度,从2到n;
i为边界起始点,从1到n-j+1;
k为分界(长度),从i后1个到j-1个后分,俩块区域的最优解合并。

#include<cstdio>
int min(int a,int b){
	if(a<b) return a;
	return b;
}
int main(){
	int n,s[101]={0},a[101][101];
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		int a;
		scanf("%d",&a);
		s[i]=s[i-1]+a;
	}
	for(int j=2;j<=n;++j)
        for(int i=1;i<=n-j+1;++i){
  	        a[i][j]=10000000;
  	        for(int k=1;k<j;++k)
  	            a[i][j]=min(a[i][k]+a[i+k][j-k]+s[i+j-1]-s[i-1],a[i][j]);
        }
    printf("%d",a[1][n]);
}

猜你喜欢

转载自blog.csdn.net/qq_42937087/article/details/84861769