【题意简述】:将一块长木板切割成N块。准备切成的木板的长度为L1、L2、……LN,未切割前模板的长度恰好为切割后模板的长度总和。求切割的花费。
例如:长度为21的木板要成长度为5、8、8的三块木板。长21的木板切成长为13和8的板时,开销为21。再将长度为13的板切成长度为5和8的板时,开销为13因此合计开销就是34。
限制条件:1 <= N <=20000
0 <= Li <=50000
【解题思路】:拿到这样一道题,不难了解。切割的方法可以用一颗二叉树来描述!这里每一个叶子的节点就对应了切割出的一块块木板。叶子节点的深度就对应了为了得到对应木板所需的切割次数,开销的合计就是各叶子节点的
木板的长度 *节点的深度
的总和。
所以最佳的切割方法所具有的性质就是这样的:
最短的板 与 此段的板的节点应当是兄弟节点。
对于最优解来说,最短的板应该是深度最大的叶子节点之一。所以与这个叶子节点同一深度的兄弟节点一定存在,并且由于同样是最深的叶子节点,所以应该对应于次短的板。
因此我们便可以将Li 按照大小排序,最短的是L1,次短的是L2。如果在二叉树中就是兄弟节点,就以为这他们是从一块长度为(L1+L2)的木板上切割下来的。
至此我们便可以通过STL中的优先队列来解决此题。详见代码:
// stl 优先队列
// 480K 32Ms
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
//比较规则,最小优先!
class cmp
{
public:
bool operator()(const __int64 a,const __int64 b)const
{
return a>b;
}
};
int main(void)
{
int n; //木板的个数
while(cin>>n)
{
priority_queue<__int64,vector<__int64>,cmp>Queue;//定义优先队列
for(int i = 1;i<=n;i++)
{
__int64 temp;
scanf("%I64d",&temp);
Queue.push(temp);//输入要求的木板长度(费用)并入队。
}
__int64 mincost = 0;//最小费用
while(Queue.size()>1)//当队列中的小于等于一个元素时跳出
{
__int64 a = Queue.top();
Queue.pop();
__int64 b = Queue.top();//两次取得队首的元素,即两个最小值!
Queue.pop();
Queue.push(a+b);//入队
mincost += a+b;
}
printf("%I64d\n",mincost);
while(!Queue.empty())//清空队列
Queue.pop();
}
return 0;
}
另外,此题还可应用Huffman 编码的思想俩解决,读者可以先去了解一下Huffman编码的思想,
Huffman Tree 也称为最优二叉树。我们可以将木板对应的换成字符,长度换成频度就可以了,这样在生成的儿茶数中,从根出发,走向左边就将0,走向右边就将1追加到编码的末尾,到达每个叶子节点时就能得到该节点对应的码字了。