R7-3 修理牧场
分数 20
作者 DS课程组
单位 浙江大学
农夫要修理牧场的一段栅栏,他测量了栅栏,发现需要N块木头,每块木头长度为整数Li个长度单位,于是他购买了一条很长的、能锯成N块的木头,即该木头的长度是Li的总和。
但是农夫自己没有锯子,请人锯木的酬金跟这段木头的长度成正比。为简单起见,不妨就设酬金等于所锯木头的长度。例如,要将长度为20的木头锯成长度为8、7和5的三段,第一次锯木头花费20,将木头锯成12和8;第二次锯木头花费12,将长度为12的木头锯成7和5,总花费为32。如果第一次将木头锯成15和5,则第二次锯木头花费15,总花费为35(大于32)。
请编写程序帮助农夫计算将木头锯成N块的最少花费。
输入格式:
输入首先给出正整数N(≤10000),表示要将木头锯成N块。第二行给出N个正整数(≤50),表示每段木块的长度。
输出格式:
输出一个整数,即将木头锯成N块的最少花费。
输入样例:
8
4 5 1 2 1 3 1 1
输出样例:
49
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB
R7-3 修理牧场 解题分项
(前提说明:本人菜菜,如有帮助那不胜荣幸,觉得屎山正常,有更好的解题思路感谢)
本题应该用哈弗曼树去解,原因有以下几点:
1.其数据形式适合用树去存储,符合其存储形态;
2.其要求求解得是非叶子结点的最小和,适合用其算法解决;
如何实现哈夫曼树?
最开始的时候想用数组去保存叶结点,然后用树去描述关系,后来想想太麻烦,费脑子,于是就准备直接用数组去存,然后把所有的非叶给加起来。
根据哈夫曼树的算法,首先应该将最小的两个数和成一个树,根节点为两者的和,再用这两者的和加入数组继续比较,且因为我们要求的是非叶子节点的总和,所以每次获得非叶子结点就可以给他加进要求的数里。
其逻辑归纳为{排序->找到最小的两个数->获得其根节点也即为两个的数的和->将两个数的和加入所有非叶子结点数和->将两个数的和插入数组里->重复1,直到最后一个数。}
具体实现中,为了保证其整体的有序性,所以准备在插入的时候便让它有序插入,为了保证速度写了二分法插入。
也因为插入所以会导致其实际队伍越来越长,其不断的向后扩展,导致其空间占用高,这应该也可以优化。
不说了下一道题。
#include <bits/stdc++.h>
using namespace std;
//冒泡排序算法
void MPsort(int eve[],int cout)
{
int a;
for(int i=0; i<cout-1; i++)
{
for(int i1=0; i1<cout-1; i1++)
{
if(eve[i1]>eve[i1+1])
{
a=eve[i1];
eve[i1]=eve[i1+1];
eve[i1+1]=a;
}
}
}
}
int EFFind(int mid,int left,int right,int *eve,int sum){
int numberins;
if(sum<=eve[left]){
numberins=left;
}
else if(sum>=eve[right]){
numberins=right+1;
}
else{while(1){
mid=(left+right)/2;
if(sum>=eve[mid]&&sum<=eve[mid+1]){
numberins=mid+1;
break;
}
else if(sum<=eve[mid]&&sum>=eve[mid-1]){
numberins=mid;
break;
}
else if(sum==eve[mid]){
numberins=mid;
break;
}
else if(sum>eve[mid]){
left=mid+1;
}
else if(sum<eve[mid]){
right=mid-1;
}
}}
return numberins;//类似二分查找的找插入点
}
int main()
{
int cout;
scanf("%d",&cout);//存储一共切成几块;
int eve[20001];//建立一个数组用来存储每块木块的长度;
for(int i=0; i<cout; i++)
{
scanf("%d",&eve[i]);//存储切成块的长度
}
//这个题应该是用哈夫曼树来解决,因为其很符合哈夫曼树的特性与形态结构,所以先写个哈夫曼树。
//哈夫曼树要先挑最小的。
//对于树的存储结构我决定用数组去存储,比较直观,而且方便运算。
//感觉用树存储很麻烦,不如直接用数组去解决。
//排序采用最简单的冒泡排序;其实感觉排序的数组用链表存储更好?因为方便插入,后面查找写个二分的话算法效率会更快?
//数组的话插入麻烦。数组写起来简单,看看能不能过。
MPsort(eve,cout);
//✖排序后进入建树阶段,虽然是用数组存储,但是其不是完全二叉树,所以为了让其更好理解便在结构体上加上了辅助的指代;
//✔后来决定不建树了,建树后其描述树的关系太过复杂,倒不如直接用数组去做。哈夫曼树不一定要写树的数据结构,重点在于其思想。
//如何实现哈夫曼树呢?
/*首先要挑出两个最小的拿出来构成一个树,根节点为两个的和。
然后需要对他的和进行排序放到数组里。
然后不断运行,直到构建到最后一个数。
最后我们要求的是所有根节点的和。
*/
//所以由于3先保证复用性;
int sum=0;//根节点
int all_sum=0;//所有根节点的和
int left=0;//二分的左右
int right=0;
int numberins,mid=0;
for(int i=0;i<cout-1;i=i+2){//因为后边+1了,所以截止到cout-1
sum=eve[i]+eve[i+1];//sum为根节点的值,左右为eve[I]与i+1;
all_sum=all_sum+sum;//所求值
//然后对其sum放到数组里,也就是执行一个数组的插入,而且这个数组还是有序的,那就二分;
//二分是i+2到cout,这里需要找到一个sum的最小区间
left=i+2;
right=cout-1;
//这个和二分查找的不同点在于,二分找不到就算了,这个找不到头就加头上或者尾上。
//但这个有个缺点,就是其占用的空间很大,因为我的数组不断的向后伸长而不是重写,后面不行的话再优化;
numberins=EFFind(mid,left,right,eve,sum);
//因为这个东西调试了整整一天,难蚌。原因有两个,一个是不熟练,另一个是没找好道路。
cout++;
if(numberins<right){
for(int g=cout;g>numberins;g--){
eve[g]=eve[g-1];
}}
eve[numberins]=sum;
}
printf("%d",all_sum);
}