#include <iostream> using namespace std; class StoneMerge { private: int **result;//动态规划记录result[i][j]表示从i起逆时针数j个的合并最大得分 int num;//有num堆石子 int *weight;//记录num堆石子的重量 int totalScore;//记录总分 public: //构造函数 StoneMerge(int num) { totalScore=0; this->num=num; result=new int* [num+1]; weight=new int[num+1];//weight[1:num] for(int i=0;i<=num;i++) { result[i]=new int[num+1]; //result[1:num][1:num] } } //输入num堆石子的重量 void input() { for(int i=1;i<=num;i++) { cout<<"输入第"<<i<<"堆的重量:"; cin>>weight[i]; } } //动态规划求解圈型石子合并问题 //result[i][j]=max{ result[i][k]+result[(i+k)%num][j-k]+sum(i,j) } , 1<=k<=j-1 //result[i][j]表示从i数j堆合并的分数,等于从k处断开而形成的i数k的堆得分数+(i+k)%num数j-k堆得分数+这两堆合并的分数 //由于是环状,最终的堆由两个子堆合并,但是不同的起点i形成的结果都是不同的,因为我们始终 //沿着逆时针找到k进行划分,即我们最终要求result[i][num],i可以取任意值时,形成的子问题都是不同的 //一旦把圆形划分成两个子堆,那么两个子问题就变成了简单的直线型堆合并问题, 这时候公式就可以被理解了 //我们最终要求得不是某个result[i][num],而是max{result[i][num]},所以我们利用直线的公式稍作改动来适应环状的下标变化 //然后利用和直线合并同样的动态规划原理递推求得所有的result[i][num],取max{result[i][num]}即我们最终的合并的最大得分 //求和公式 int sum(int i,int j)//求从i起数j堆得石子数总和 { int localSum=0; if(i+j-1<=num) { for(int k=i;k<=i+j-1;k++) { localSum+=weight[k]; } return localSum; } else { for(int k=i;k<=num;k++) { localSum+=weight[k]; } for(int k=1;k<=(i+j-1)%num;k++) { localSum+=weight[k]; } return localSum; } } //动态规划求解 void mergeStone() { for(int i=1;i<=num;i++) { result[i][1]=0; } for(int j=2;j<=num;j++) { for(int i=1;i<=num;i++) { result[i][j]=-1; for(int k=1;k<=j-1;k++) { if(result[i][k]+result[(i+k)%num][j-k]+sum(i,j)>result[i][j]) { result[i][j]=result[i][k]+result[(i+k)%num][j-k]+sum(i,j); } } } } //从result[1:num][num]中找出最大值 for(int i=1;i<=num;i++) { if(result[i][num]>totalScore) { totalScore=result[i][num]; } } cout<<"最大合并得分:"<<totalScore<<endl; } }; void main() { StoneMerge test(4); test.input(); test.mergeStone(); }