欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[蓝桥杯][算法提高VIP]合并石子(区间dp+平行四边形优化)

程序员文章站 2022-04-01 17:55:18
...

题目描述
在一条直线上有n堆石子,每堆有一定的数量,每次可以将两堆相邻的石子合并,合并后放在两堆的中间位置,合并的费用为两堆石子的总数。求把所有石子合并成一堆的最小花费。

输入
输入第一行包含一个整数n,表示石子的堆数。
接下来一行,包含n个整数,按顺序给出每堆石子的大小 。
输出
输出一个整数,表示合并的最小花费。

样例输入
5
1 2 3 4 5
样例输出
33
思路:蓝桥杯官网好像放宽时间限制了,不加平行四边形优化也可以过,但是dotcpp网站上不行。这算是一个区间dp的入门题目,平行四边形优化可以自行百度一下,用的不多。。
代码如下:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;

const int maxx=1e3+100;
int dp[maxx][maxx];
int a[maxx];
int sum[maxx];
int n;

int main()
{
	scanf("%d",&n);sum[0]=0;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
	memset(dp,inf,sizeof(dp));
	for(int i=1;i<=n;i++) dp[i][i]=0;
	for(int l=1;l<=n;l++)
	{
		for(int i=1;i<n-l+1;i++)
		{
			int j=i+l;
			for(int k=i;k<=j;k++)
			{
				dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
			}
		}
	}
	cout<<dp[1][n]<<endl;
	return 0;
}

区间dp+平行四边形优化代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;

const int maxx=1e3+100;
int dp[maxx][maxx];
int sum[maxx];
int s[maxx][maxx]; 
int n;
int a[maxx];

void DP(int l,int r)
{
	sum[0]=a[0];
	for(int i=1;i<n;i++)
	{
		sum[i]=sum[i-1]+a[i];
		s[i][i]=i;
	}
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<n;j++)
		{
			if(i==j) dp[i][j]=0;
			else dp[i][j]=inf;
		}
	}
	
	for(int len=2;len<=n;len++)//长度 
	{
		for(int i=0;i<=n-len+1;i++)//起点 
		{
			int j=i+len-1;//终点 
			for(int k=s[i][j-1];k<=s[i+1][j];k++)//间隔点 
			{
				//dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
				if(dp[i][j]>dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1])
				{
					dp[i][j]=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1];
					s[i][j]=k;
				}	
			}
		}
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=0;i<n;i++) scanf("%d",&a[i]);
	DP(0,n);
	printf("%d\n",dp[0][n-1]);
}

努力加油a啊,(o)/~