POJ 3253 Fence Repair 哈夫曼树

哈夫曼树是一种特殊的二叉树,是一种带权路径长度最短的二叉树。所谓树的带权路径长度,就是树中所有的叶结点

的权值乘上其到根结点的 路径长度(若根结点为0层,叶结点到根结点的路径长度

为叶结点的层数)。树的带权路径长度记为WPL= (W1*L1+W2*L2+W3*L3+...+Wn*Ln)

,N个权值Wi(i=1,2,...n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径

长度为Li(i=1,2,...n)。可以证明哈夫曼树的WPL是最小的。

如:


可以看出第二种wpl最小及哈夫曼树。

由于木头锯一次产生二个部分,则可以用二叉树表示,哈夫曼树则为花费最小的方式从初始木板中锯下第i段目标木板,锯木的次数为根至第i个叶点的路劲长度为Pi。锯下第i段目标木板的花费为Pi*Wi。

以n段为目标木板的长度为关键字,构建最小堆P;每次分两次取出堆首节点,权分别为a和b,合成一个权为(a+b)的节点插入最小堆p(表示总长为(a+b)的木块锯成长度为a和b的两块木块),费用ans增加(a+b)。经过n-1次合并后,堆中就剩一个结点。此时ans便为最小费用。


Fence Repair
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 32914 Accepted: 10637

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

Hint

He wants to cut a board of length 21 into pieces of lengths 8, 5, and 8. 
The original board measures 8+5+8=21. The first cut will cost 21, and should be used to cut the board into pieces measuring 13 and 8. The second cut will cost 13, and should be used to cut the 13 into 8 and 5. This would cost 21+13=34. If the 21 was cut into 16 and 5 instead, the second cut would cost 16 for a total of 37 (which is more than 34).

Source

[Submit]   [Go Back]   [Status]   [Discuss]

ACcode:

#pragma warning(disable:4786)//使命名长度不受限制
#pragma comment(linker, "/STACK:102400000,102400000")//手工开栈
#include <map>
#include <set>
#include <queue>
#include <cmath>
#include <stack>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
#define rd(x) scanf("%d",&x)
#define rd2(x,y) scanf("%d%d",&x,&y)
#define rds(x) scanf("%s",x)
#define rdc(x) scanf("%c",&x)
#define ll long long int
#define maxn 100005
#define mod 1000000007
#define INF 0x3f3f3f3f //int 最大值
#define FOR(i,f_start,f_end) for(int i=f_start;i<=f_end;++i)
#define MT(x,i) memset(x,i,sizeof(x))
#define PI  acos(-1.0)
#define E  exp(1)
using namespace std;
ll n,len,ans,x,y,p[maxn];
void heap_insert(ll k){///插入最小堆
    ll 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(){///删除最小堆的的首结点
    ll t=1;
    p[t]=p[len--];///将堆尾的值调整到堆首然后向下调整
    while(t<<1<=len){
        ll k=t<<1;///计算儿子中较小的结点序号
        if(k<len&&p[k]>p[k+1])++k;///判断往左儿子还是往右儿子交换
        if(p[t]>p[k]){///如果堆结点k的值小于儿子结点交换
            swap(p[t],p[k]);
            t=k;
        }else break;
    }
}
int main(){
    rd(n);len=ans=0;
    FOR(i,1,n){rd(p[i]);heap_insert(p[i]);}
    while(len>1){
        x=p[1];heap_pop();
        y=p[1];heap_pop();
        ans+=x+y;
        heap_insert(x+y);
    }
    printf("%lld\n",ans);
    return 0;
}
/*
3
8
5
8
*/

其实这题还有一种贪心的做法

对于最优解来说,最短的板当是深度最大的叶子结点之一。所以与这个叶子节点同一深度的兄弟节点一定存在,并且由于同样是最深的叶子节点,所以应该对应于次短的板。不妨将Li按照大小顺序排序,那么最短的板应该是L1而次短的则是L2。如果它们在二叉树中是兄弟结点,就意味着它们是又(L1+L2)长的板锯来。由于切割顺序是自由的,不妨当作是最后切割。那么此时就有长为(L1+L2),L3,L4……,LN的N-1块木板存在。递归求解N-1就能得到答案。

ACcode:

#pragma warning(disable:4786)//使命名长度不受限制
#pragma comment(linker, "/STACK:102400000,102400000")//手工开栈
#include <map>
#include <set>
#include <queue>
#include <cmath>
#include <stack>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
#define rd(x) scanf("%d",&x)
#define rd2(x,y) scanf("%d%d",&x,&y)
#define rds(x) scanf("%s",x)
#define rdc(x) scanf("%c",&x)
#define ll long long int
#define maxn 100005
#define mod 1000000007
#define INF 0x3f3f3f3f //int 最大值
#define FOR(i,f_start,f_end) for(int i=f_start;i<=f_end;++i)
#define MT(x,i) memset(x,i,sizeof(x))
#define PI  acos(-1.0)
#define E  exp(1)
using namespace std;
ll n,a[maxn];
int main(){
    rd(n);FOR(i,1,n)rd(a[i]);
    sort(a+1,a+1+n);
    ll ans=0;
    while(n>1){
        int x=1,y=2;///最短的板和次短的板
        if(a[x]>a[y])swap(x,y);
        FOR(i,3,n)
            if(a[i]<a[x]){y=x;x=i;}
            else if(a[i]<a[y])y=i;
        ll t=a[x]+a[y];
        ans+=t;
        if(x==n)swap(x,y);
        a[x]=t;a[y]=a[n--];
    }
    printf("%lld\n",ans);
    return 0;
}


可以看出用哈夫曼树的速度是快很多的



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值