算法设计与分析第三章作业

软工2103叶伟杰 2022-11-14 23:58:18

一、请写出以下题目的动态规划方程:

设有 N堆石子排成一排,其编号为 1,2,3,…,N。

每堆石子有一定的质量,可以用一个整数来描述。用数组a[N]表示每堆石子的质量,sum[i,j]表示第i堆石子到第j堆石子的总质量

现在要将这 N堆石子合并成为一堆。每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同。

例如有 4堆石子分别为 1 3 5 2, 我们可以先合并 1、2 堆,代价为 4,得到 4 5 2, 又合并 1,2 堆,代价为 9,得到 9 2 ,再合并得到 11,总代价为 4+9+11=24;

如果第二步是先合并 2,3 堆,则代价为 7,得到 4 7,最后一次合并代价为 11,总代价为 4+7+11=22。

问题是:找出一种合理的方法,使总的代价最小,输出最小代价。

首先 N堆石子,可以看作2份N/2堆的石子各自合并的问题,再将2份石子合并一次得解,符合重叠子问题性质。

其次,原问题N堆石子的解取决于2份N/2堆石子的合并问题的最优解,符合最优子结构。

则我们可以分析出:

状态表示:dp[i][j] 表示第i堆石子到第j堆石子合并的最小总代价;

状态方程:dp[i][j] = min( dp[i][j] , dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1] )        1<=i<k<j<=N;//sum为前缀和数组辅助;

边界条件:dp[i][j]=0   i = j ;

时间复杂度:O(n^3)

空间复杂度:O(n^2)

附上代码:

#include<iostream>
using namespace std;
int dp[10010][10010];
int sum[10010];
int stone[10010];
int main()
{
    int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>stone[i];
		sum[i]=sum[i-1]+stone[i];
	}
    for(int len=1;len<=n;len++)
    {
	    for(int i=1;i+len-1<=n;i++)
        {
    		int j=i+len-1;
    		if(len==1)
    			dp[i][j]=0;
			else
            {
				for(int k=i;k<j;k++)
                {
					if(dp[i][j]!=0)
						dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
					else
						dp[i][j]=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1];
				}
			} 
		}
	}
	cout<<dp[1][n]<<endl;
	return 0;
}

二.、结合本章的学习,总结你对动态规划法的体会和思考

动态规划——总结起来就是比分治法应用更为广泛,比贪心算法更为全面,代码量不多,但是状态量难定义,状态转移方程较难推导,边界条件需要分析。

动态规划的问题一般满足下面条件:

问题是否具有重叠子问题性质和最优子结构;

根据问题推论出状态定义和转移方程;

最后判断一些数据大小,大部分的二维数组的dp可能会受到内存的限制,考虑是否能简化成一维数组进行处理;

动态规划也有许多的变形,合理的运用前缀和数组。

...全文
51 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

434

社区成员

发帖
与我相关
我的任务
社区描述
广东外语外贸大学信息科学与技术学院
算法 高校
社区管理员
  • brisksea
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧