石子相邻合并(直线型)求和问题得分最小,矩阵连乘积问题

石子相邻合并(直线型)求和问题得分最小,矩阵连乘积问题
这两个问题是同一个问题
解法:本文采用动态规划的方式求解
思路:从枚举法衍生的动态规划法
看法:有两种:1、石子合并中因为是取相邻的合并所以,得分最小的一种贪心思维就是求出每次合并的堆(eg:求出1、2 堆,2、3堆合并值在选择最小的作为本次的合并)的所有值,选择最小的作为本次合并值,即可求出解。
2、可以看成最后的结果是将石子分成两堆。

  • 如何划分呢? 

  按照这两堆的合并时候得分最小来划分(称它为最优划分,划分对应断开位置称最优划分点),

  • 为什么分成两部分来计算答案是对的呢?

  就和题目叙述的“相邻合并”有关系了,按照1方法最终合并就是两堆,但是可以发现这两堆(设为A,B)是相互没有联系的不可能说A中得分会用到B中某一堆的石子如果用到他应该变成A那一部分的,所以题目就变成了分治法,我可以每次把当前段划分称两部分缩小范围最终当1:n间距是2的时候就很容易求解出来了。

  •   问题这是分治算法啊,不是动态规划啊,如何用动态规划的想法求解啊?

  可以发现这种算法有很多重复计算对不,这就是说明可以优化,
  因为它要用到下X(i:j)的最优值,显然i,j距离为3是会用到i,j距离为2的和或者为1的值。直接用公式来说就是
  X(i:j)=min( ( x(i)+x(i+1:j)+m(x(i+1:j)) ) ,      x(i:i+1)+m(x(i:i+1))+x(i+2:j)+m(x(i+2:j)) ,       x(i:i+2)+m(x(i:i+2))+x(i+3:j)+m(x(i+3:j)) ),......,        x(i:j-1)+m(x(i:j-1))+x(j)    )

所以我根据i,j的距离划分阶段,用i的值作为对应状态可以构架当距离是i-j=n,i=1的时候就是最终的解了,

分治法代码

代码:

//分治
#include<iostream>
using namespace std;
#define max  1000
int m[max]={0};//存石子数的数组 

int sum(int i,int j)//求每堆石子的总数函数 
{
	int stone_sum=0;
	for(int k=i;k<=j;k++)
	stone_sum=stone_sum+m[k];
	return stone_sum;
	
}

int divide_and_conquer(int i,int j)//递归求解i,j最优的值 
{
	int nowX=0,bestX=100000;
	if(j-i<=0)//终止条件 
	return 0;
	else
	for (int k=i;k<j;k++)//寻找出ij最优值 
	{
	nowX=divide_and_conquer(i,k)+divide_and_conquer(k+1,j)+sum(i,k)+sum(k+1,j);
	//divide_and_conquer(k+1,j)求x(k+1,j) 的得分,sum(i,k)+sum(k+1,j)求本次应该加的分 
		if(bestX>nowX)
		bestX=nowX;
	}
	return bestX; 

 } 
 
 int main()
 {
 	int amount=0;
 	cin>>amount;
 	for(int i=1;i<=amount;i++)
 	cin>>m[i];
 	cout<<"1:n堆的最小值是:"<<divide_and_conquer(1,amount)<<endl;
 	return 0;
 	
 	
  } 

时间复杂度:n阶乘,所以过不了任何题目的,(大概估计的不知道准不准,反正一定n阶乘

备忘录法代码
 

//备忘录法 
#include<iostream>
using namespace std;
#define max  1000
int m[max]={0};//存石子数的数组 
int mm[max][max]={0}; 

int sum(int i,int j)//求每堆石子的总数函数 
{
	int stone_sum=0;
	for(int k=i;k<=j;k++)
	stone_sum=stone_sum+m[k];
	return stone_sum;
	
}

int divide_and_conquer(int i,int j)//递归求解ij最优的值 
{
	int nowX=0,bestX=100000;
	if(j-i<=0)//终止条件
		return 0; 
	if(mm[i][j]!=0)
	return mm[i][j];

	else
	{for (int k=i;k<j;k++)//寻找出ij最优值 
	{
	nowX=divide_and_conquer(i,k)+divide_and_conquer(k+1,j)+sum(i,k)+sum(k+1,j);
	//divide_and_conquer(k+1,j)求x(k+1,j) 的得分,sum(i,k)+sum(k+1,j)求本次应该加的分 
		if(bestX>nowX)
		bestX=nowX;
	}
     mm[i][j]=bestX; 
	return bestX; 
}

 } 
 
 int main()
 {
 	int amount=0;
 	cin>>amount;
 	for(int i=1;i<=amount;i++)
 	cin>>m[i];
 	cout<<"1:n堆的最小值是:"<<divide_and_conquer(1,amount)<<endl;
 	return 0;
 	
 	
  } 

时间复杂度:n的3次方

动态规划法代码

//动态规划法 
#include<iostream>
using namespace std;
#define max  1000
int m[max]={0};//存石子数的数组 
int mm[max][max]={0};

int sum(int i,int j)//求每堆石子的总数函数 
{
	int stone_sum=0;
	for(int k=i;k<=j;k++)
	stone_sum=stone_sum+m[k];
	return stone_sum;
	
}
int DP(int i,int j)//递归求解ij最优的值 
{
for(int distance=1;distance<=j-i;distance++)//求出j-i=0 1 2 3对应的mm[ki][kj]的值 
	for(int ki=1;ki<=j-distance;ki++)// 每种距离下ki的可能取值 
	{
		int kj=ki+distance;//求出ki对应的kj 
		int bestX=10000;
		int nowX=0;
		for(int k=ki;k<kj;k++)//求出每种ki,kj的最优值 
		{
			nowX=mm[ki][k]+mm[k+1][kj]+sum(ki,kj);
			if(bestX>nowX)
			bestX=nowX;
		}
		mm[ki][kj]=bestX;
	
	 } 
	 /*for(int ki=1;ki<=j;ki++)
	 {
	 	for(int kj=1;kj<=j;kj++)
	 cout<<mm[ki][kj]<<'\t';
	 cout<<endl;
	 }
	 */

return mm[i][j];
}
 int main()
 {
 	int amount=0;
 	cin>>amount;
 	for(int i=1;i<=amount;i++)
 	cin>>m[i];
 	
 	cout<<DP(1,amount)<<endl;
 	return 0;
 	
 	
  } 

```

时间复杂度:n的3次方
  第一种思路贪心算法下次写吧
  

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值