文章目录
石子合并问题
Problem Description
在一个圆形操场的四周摆放着n堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。试设计一个算法,计算出将n堆石子合并成一堆的最小得分和最大得分。
对于给定n堆石子,计算合并成一堆的最小得分和最大得分。
Input
输入数据的第1行是正整数n,1≤n≤100,表示有n堆石子。第二行有n个数,分别表示每堆石子的个数。
Output
输出数据有两行,第1行中的数是最小得分,第2行中的数是最大得分。
Sample Input
4
4 4 5 9
Sample Output
43
54
这里我都以求最小值为例子了
1.任意合并
先考虑最简单的情况,随便取两堆合并,每次选最小的两段合并,就是哈夫曼树的变形。
stl堆实现
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
/*void display(vector<int>q) {
for (int i = 0; i < q.size(); i++) {
cout << q[i] << " ";
}
cout << endl;
}*/
int main()
{
int n;
cin>>n;
vector<int> v;
while(n--){
int x;
cin>>x;
v.push_back(x);
}
long long sum=0;
make_heap(v.begin(),v.end(),greater<int>());//构建最小值堆
while(v.size()>1)
{
int min1=v.front();
pop_heap(v.begin(),v.end(),greater<int>());//除去最小值后再次构建最小值堆
//display(v);
v.pop_back();//其实这里是真的去掉最小值
int min2=v.front();
pop_heap(v.begin(),v.end(),greater<int>());
v.pop_back();
//cout<<min1<<" "<<min2<<endl;
sum+=(min1+min2);
v.push_back(min1+min2);//将合并后的值继续放入堆中
push_heap(v.begin(),v.end(),greater<int>());
}
cout<<sum<<endl;
}
return 0;
}
2.只能相邻合并(不是环形)
我们熟悉矩阵连乘,知道矩阵连乘也是每次合并相邻的两个矩阵,那么石子合并可以用矩阵连乘的方式来解决。
设dp[i][j]表示第i到第j堆石子合并的最优值,sum[i][j]表示第i到第j堆石子的总数量。那么就有状态转移公式:
#include <iostream>
using namespace std;
int main()
{
int n;cin>>n;
int a[n+1];//用a[1]到a[n]表示n堆石头的个数
for(int i=1;i<=n;++i)
cin>>a[i];
int dp[n+1][n+1];//dp[i][j]表示从第i堆到第j堆合并的代价
int sum[n+1][n+1];//表示石头的数量
for(int i=1;i<=n;++i)//初始化
{
dp[i][i]=0;
sum[i][i]=a[i];
}
for(int len=2;len<=n;++len)//区间长度
for(int i=1;i<=n-len+1;++i)//区间开始下标
{
int j=i+len-1; //区间结束下标
sum[i][j]=sum[i][i]+sum[i+1][j];//相当于就是k=i的时候
dp[i][j]=dp[i][i]+dp[i+1][j]+sum[i][j];
for(int k=i+1;k<j;++k)
{
sum[i][j]=sum[i][k]+sum[k+1][j];
int t=dp[i][k]+dp[k+1][j]+sum[i][j];
if(t<dp[i][j])
dp[i][j]=t;
}
}
//这个方法是按照自己算法书上套的 写法稍微有点麻烦
cout<<dp[1][n]<<endl;
}