蓝桥杯 合并石子 DP+四边形不等式优化

  算法提高 合并石子  
时间限制:2.0s   内存限制:256.0MB
    
问题描述
  在一条直线上有n堆石子,每堆有一定的数量,每次可以将两堆相邻的石子合并,合并后放在两堆的中间位置,合并的费用为两堆石子的总数。求把所有石子合并成一堆的最小花费。
输入格式
  输入第一行包含一个整数n,表示石子的堆数。
  接下来一行,包含n个整数,按顺序给出每堆石子的大小 。
输出格式
  输出一个整数,表示合并的最小花费。
样例输入
5
1 2 3 4 5
样例输出
33
数据规模和约定
  1<=n<=1000, 每堆石子至少1颗,最多10000颗。
 

思路:和蓝桥杯另一个题目 矩阵乘法 同一个类型,考虑用DP求解,dp[i][j]表示把第i堆到第j堆合到一起的最小花费,则有状态转移方程:


其中sum[i][j]代表第i堆到第j堆的总石子数,可以用一个前缀和数组代替。

本以为这题就这么过了,但没想到第一发只过了7个点,剩下三个超时,然后无论我怎么想办法优化,都是卡在最后一个点上,最后我把状态转移过程中的min库函数换掉,再把对数组的访问和修改换成对整数修改,最后再赋值给dp数组,终于1900+ms擦线过了。。但我感觉这题应该有更好的方法,百度一下果然不出所料,也因此学到新知识----四边形不等式优化DP

关于四边形不等式优化DP的方法及证明请戳这里。

下面是压线过的代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define pi acos(-1)
#define inf 0x3f3f3f3f
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
typedef pairP;
const int MAXN=100010;
int dp[1001][1001]; 
int a[1010],s[1010][1010];
int main()
{
	//memset(dp,inf,sizeof(dp));//用memset这题也能勉强过,但是如果用下面注释掉的那三行进行状态转移就会卡在最后一个点上。
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	scanf("%d",a+i),dp[i][i]=0,a[i]+=a[i-1];
	for(int len=2;len<=n;len++)
	{
		for(int i=1,j=len;j<=n;i++,j++)
		{
			int temp=inf;
			dp[i][j]=inf;
			for(int k=i;k<=j;k++)
			{
				int t=dp[i][k]+dp[k+1][j];
				if(temp>t)
				temp=t;
//				int temp=dp[i][k]+dp[k+1][j]+a[j]-a[i-1];//用这三行的话会卡在最后一个点,但其实这已经是优化过的版本了,
//				if(temp
然后是四边形不等式优化,直接将时间降到了31ms。。

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define pi acos(-1)
#define inf 0x3f3f3f3f
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
typedef pairP;
const int MAXN=100010;
int dp[1001][1001]; 
int a[1010],s[1010][1010];
int main()
{
	memset(dp,inf,sizeof(dp));
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	scanf("%d",a+i),dp[i][i]=0,a[i]+=a[i-1],s[i][i]=i;
	for(int len=2;len<=n;len++)
	{
		for(int i=1,j=len;j<=n;i++,j++)
		{
			for(int k=s[i][j-1];k<=s[i+1][j];k++)
			{
				int temp=dp[i][k]+dp[k+1][j]+a[j]-a[i-1];
				if(temp

最后,关于合并石子问题还有一种专门的算法----- GarsiaWachs算法,朴素实现的复杂度为O(n^2),若加上平衡树优化为O(nlogn ),但是蒟蒻并不会平衡树,而且网上并没有很多关于这个算法的证明,大多数讲这个算法的都是讲如何朴素的实现,但就算是朴素的实现我看起来也很费劲。。而且这个算法貌似并没有其他方面的应用,就先不深入研究了吧。

讲解GarsiaWachs算法的博客:http://blog.csdn.net/huzhengnan/article/details/7018116



你可能感兴趣的:(====DP====,蓝桥杯)