Sweety

Practice makes perfect

导航

(哈夫曼树经典)A - Fence Repair(10.3.1)

Posted on 2014-08-04 14:36  蓝空  阅读(180)  评论(0编辑  收藏  举报

 

Description

Farmer John wants to repair a small length of the fence around the pasture. He measures the fence and finds that he needs N (1 ≤ N ≤ 20,000) planks of wood, each having some integer length Li (1 ≤ Li ≤ 50,000) units. He then purchases a single long board just long enough to saw into the N planks (i.e., whose length is the sum of the lengths Li). FJ is ignoring the "kerf", the extra length lost to sawdust when a sawcut is made; you should ignore it, too.

FJ sadly realizes that he doesn't own a saw with which to cut the wood, so he mosies over to Farmer Don's Farm with this long board and politely asks if he may borrow a saw.

Farmer Don, a closet capitalist, doesn't lend FJ a saw but instead offers to charge Farmer John for each of the N-1 cuts in the plank. The charge to cut a piece of wood is exactly equal to its length. Cutting a plank of length 21 costs 21 cents.

Farmer Don then lets Farmer John decide the order and locations to cut the plank. Help Farmer John determine the minimum amount of money he can spend to create the N planks. FJ knows that he can cut the board in various different orders which will result in different charges since the resulting intermediate planks are of different lengths.

Input

Line 1: One integer N, the number of planks
Lines 2..N+1: Each line contains a single integer describing the length of a needed plank

Output

Line 1: One integer: the minimum amount of money he must spend to make N-1 cuts

Sample Input

3
8
5
8

Sample Output

34

 

本题就是一个经典的哈夫曼树,其实自己感觉哈弗曼树的精髓不是权值*路径长度这个特点,而是在于要想让这个乘积最小的根源是他被加了多少次,大的数就应该减少他被加的次数才是实质。

本题就是每一次让当前的最小的两个值相加,然后将前两个节点删掉就行不过不是一次性的将所有的最小值相加,然后在让这些值再一轮的相加,不是这样循环,而是每一次在前一轮的相加之后,就再一次排序,无论是不是上一轮相加还是没相加(其实本题可以想成是将所有的木块接起来)

 

本题中虽然总的木棍长度不是很大,但是在每一次结截完之后就大了,所以由于一开始的时候没有注意,WR了好多次,所以一定注意用long long(注意long long 的使用)

课本代码:

#include <iostream>
using namespace std;
const long maxn=20000+10;
long n,len;
long long p[maxn];
void heap_insert(long long k)
{
	long t=++len;
	p[t]=k;
	while(t>1)
	{
		if(p[t/2]>p[t])
		{
			swap(p[t],p[t/2]);
			t/=2;
		}
		else
			break;
	}
}
void heap_pop(void)
{
	long t=1;
	p[t]=p[len--];
	while(t*2<=len)
	{
		long k=t*2;
		if(k<len&&p[k]>p[k+1])
			++k;
		if(p[t]>p[k])
		{
			swap(p[t],p[k]);
			t=k;
		}
		else
			break;
	}

}
int main()
{
	cin>>n;
	for(long i=1;i<=n;i++)
		cin>>p[i];
	len=0;
	for(long i=1;i<=n;i++)
		heap_insert(p[i]);
	long long ans=0;
	while(len>1)
	{
		long long a,b;
		a=p[1];
		heap_pop();
		b=p[1];
		heap_pop();
		ans+=a+b;
		heap_insert(a+b);
	}
	cout<<ans<<endl;
	return 0;
}


注意priority_queue 的用法

自己代码:

#include<stdio.h>
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;

int main()
{
	priority_queue<__int64, vector<__int64>, greater<__int64> >q;
	int n;  
	scanf("%d",&n);
	int i=n;
	int l;
	while(n--)
	{ 
		scanf("%d",&l);
		q.push(l);
	}
	__int64  x, s=0;
	if(i==1)             //本if语句不需要,以开始的时候没仔细想,就把为1的情况另外讨论了,但是这样是错误的,当为1的时候是不需要付钱的,但是这                                         //     样写提交的时候也是正确的,这是POJ的bug。。。
	{
		scanf("%d",&q.top());
		// cout<<q.top();
		return 0;
	}
	while(   !( q.empty() )   )
	{ 
		x=q.top();
		q.pop();
		if( q.empty() )
			break; 
		__int64 w=q.top();
		q.pop();
		x = w + x; 
		q.push(x);
		s=s+x;  
	}
	printf("%I64d\n",s) ;
	return 0;
}


自己代码的话,是用了STL中priority_queue的库函数,但是由于当时不知道怎么结束循环,就用了break,显得好笨啊,其实可以使用本库函数中的size()函数实现更好的控制循环

while(q.size()>1)//终止条件为  最后合为 一块 木头
	{
        __int64 l1,l2;
		l1=q.top();
		 q.pop();
		l2=q.top();
		 q.pop();

		l=l1+l2;
		sum+=l;
		q.push(l);//把 长度 l 加到 队列中

	}