石子相邻合并(直线型)求和问题得分最小,矩阵连乘积问题
这两个问题是同一个问题
解法:本文采用动态规划的方式求解
思路:从枚举法衍生的动态规划法
看法:有两种: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次方
第一种思路贪心算法下次写吧