题述
农夫约翰为了修理栅栏,要将一块很长的模板切割成N块。准备切成的模板的长度为L1、L2、L3、…、LN,未切割前木板的长度恰好为切割后木板长度的总和。每次切断木板时,需要的开销为这块木板的长度。例如长度为21的木板要切成长度为5、8、8的三块木板。长度为21的木板切成长为13和8的板时,开销为21.再将长度为13的木板切成长度为5和8的板时,开销是13.于是合计开销为34.请求出按照目标要求将木板切割完最小的开销是多少?
限制条件
1<=N<=20000
0<=Li<=50000
题记
这样的切割方法其实类似于二叉树
这里的每一个叶子结点就对应了切割出的一块块木板。叶子结点的深度就对应了为了得到对应木板所需的切割次数,开销的合计就是各叶子的结点木板长度*结点的深度的总和。这道题其实就是最优二叉树(赫夫曼树)的变形。每次选出权重最小的两个结点,权重相加作为新的结点,以此类推构造出来的二叉树就是最优树。
遇到的问题
最后合并的时候,原来的最小和次小结点(min1和min2)已经没用了,我们需要把新的结点(两个木板的长度)加进去,我们采用从后往前缩小,我们假定让L[min1]存储新结点,L[min2]存储最后一个节点L[N-1],这里有各种特殊情况:万一min2的值等于N-1,那么这个结点就被我们自动隐藏了,所以需要把min1和min2换一下,这样万一min2等于N-1呢?其实正好,因为min2指向的结点是之后用不上的,所以L[N-1]被隐藏掉也没事。
朴素的方法代码如下
#include <iostream>
//修理栅栏
using namespace std;
const int Maxn=20005;
typedef long long ll;
//输入
int N,L[Maxn];
void solve(){
ll ans=0;
while(N>1){
//求出最短的板min1和次短板min2
int min1=0,min2=1;
if(L[min1]>L[min2])
swap(min1,min2);
for(int i=2;i<N;i++){
if(L[i]<L[min1]){
min2=min1;
min1=i;
}
else if(L[i]<L[min2]){
min2=i;
}
}
//将两块板合并起来
int t=L[min1]+L[min2];
ans+=t;
L[min1]=t; //合并之后的值赋给L[min1]
L[min2]=L[N-1]; //把最后一个木板的值赋给L[min2]
N--;
}
printf("%lld\n",ans);
}
int main()
{
scanf("%d",&N);
for(int i=0;i<N;i++)
scanf("%d",&L[i]);
solve();
return 0;
}
使用优先队列解决
优先队列知识补充
priority_queue< Type,Container,Functional>
Type就是数据的类型,Container就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等不能用list,STL默认用vector),Functional就是比较的方式,当需要用自定义的数据类型时才需要传入这三个参数,使用基本类型时,只需要第一个就行了
//升序队列
priority_queue<int,vector<int>,greater<int>> que_greater;
//降序队列
priority_queue<int,vector<int>,less<int>> que_less;
具体代码如下
#include <iostream>
#include <queue>
//栅栏修补队列解决方法
using namespace std;
typedef long long ll;
const int Maxn=20005;
//输入
int N,L[Maxn];
void solve(){
ll ans=0;
//升序队列
priority_queue<int,vector<int>,greater<int>> que;
for(int i=0;i<N;i++){
que.push(L[i]);
}
//循环到只有一个木板为止
while(que.size()>1){
//取出最短的木板和次短的木板
int min1,min2;
min1=que.top();
que.pop();
min2=que.top();
que.pop();
//把两个木板合并加到原来的队列里
ans+=min1+min2;
que.push(min1+min2);
}
printf("%lld\n",ans);
}
int main()
{
scanf("%d",&N);
for(int i=0;i<N;i++)
scanf("%d",&L[i]);
solve();
return 0;
}