您的位置:首页 > 其它

哈夫曼树(POJ3253)

2016-04-14 22:31 281 查看

题目

POJ3253

大概题意:XX想修补东西,然后需要用木板。可以认为XX有一根无限长(长度=他需要的木板长度总和)的木板,但是他需要N块长度为Li的木板,所以他需要把这块无限长的木板锯成他需要的。每次锯木板的花费与锯之前木板的长度相等。求最小花费!

测试案例:

3

8

5

8

34

sum=0;

开始木板长度21(8+5+8),sum+=21,要锯成13+8;

然后,需要锯的木板长度13,sum+=13,要锯成5+8;

所以sum=34.

(写代码时可以将过程反过来。)

如何确定每次锯的木板的长度,用到的就是“哈夫曼树”的思想。每次取最优,又符合贪心的思想。

但是仅仅是这样,会超时的。

第一步

我用的数组存的所有的数,每次都排序完,取前两个数的和。

毫无疑问超时。O(n^2logn)

TLE

第二步

我将数组改成链表,使用插入排序,以为可以优化.

但是,还是超时。O(n^2)

代码(TLE)

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include <set>
#include <map>
#include<list>
#include <stack>
#include <queue>
#include <vector>
#include <ctime>

#define ll long long
#define f(i,a,b) for(int i=a;i<=b;i++)
#define m(a,b) memset(a,b,sizeof(a))
#define MAX 0x3f3f3f3f
const ll MOD=1e+9+7;

using namespace std;
struct Node{
ll data;
Node* next;
};
typedef Node* linklist;
linklist head;
void assert(ll n)
{
linklist p=head;
linklist q=new(Node);
if(p->next==NULL){
q->data=n;
q->next=NULL;
p->next=q;
}
else if(n<=p->next->data){
q->data=n;
q->next=p->next;
p->next=q;
}
else {
p=p->next;
bool flag=1;
while(p->next!=NULL){
if(n<=p->next->data){
q->data=n;
q->next=p->next;
p->next=q;
flag=0;
break;
}
p=p->next;
}
if(flag){
q->data=n;
q->next=NULL;
p->next=q;
}
}
}
void del(linklist p)
{
linklist q=p->next;
p->next=q->next;
delete(q);
}
ll operate()
{
ll temp=head->next->data+head->next->next->data;
del(head);
del(head);
assert(temp);
return temp;
}

int main()
{
head=new(Node);
head->next=NULL;
int n;
cin>>n;
f(i,0,n-1){
ll a;
cin>>a;
assert(a);
}
ll ans=0;
f(i,0,n-2){
ans+=operate();
}
cout<<ans<<endl;
return 0;
}


第三步

我以为我以为就是我以为的。。。

看了别人的代码,才知道,问题处在了别的地方,但是说实话,我感觉只是测试数据不够强,不然代码同样会被卡死。为什么这么说呢,请看我改过之后的AC代码。目前对标程了解尚浅。。。

AC代码

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include <set>
#include <map>
#include<list>
#include <stack>
#include <queue>
#include <vector>
#include <ctime>

#define ll long long
#define f(i,a,b) for(int i=a;i<=b;i++)
#define m(a,b) memset(a,b,sizeof(a))
#define MAX 0x3f3f3f3f
const ll MOD=1e+9+7;

using namespace std;
struct Node{
ll data;
Node* next;
};
typedef Node* linklist;
linklist head;
void assert(ll n)
{
linklist p=head;
linklist q=new(Node);
if(p->next==NULL){
q->data=n;
q->next=NULL;
p->next=q;
}
else if(n<=p->next->data){
q->data=n;
q->next=p->next;
p->next=q;
}
else {
p=p->next;
bool flag=1;
while(p->next!=NULL){
if(n<=p->next->data){
q->data=n;
q->next=p->next;
p->next=q;
flag=0;
break;
}
p=p->next;
}
if(flag){
q->data=n;
q->next=NULL;
p->next=q;
}
}
}
void del(linklist p)
{
linklist q=p->next;
p->next=q->next;
delete(q);
}
ll operate()
{
ll temp=head->next->data+head->next->next->data;
del(head);
del(head);
assert(temp);
return temp;
}

int main()
{
head=new(Node);
head->next=NULL;
int n;
scanf("%d",&n);
int s[20020];
f(i,0,n-1){
scanf("%d",&s[i]);
}
sort(s,s+n);
linklist p=head;
f(i,0,n-1){
linklist q=new(Node);
q->data=s[i];
q->next=NULL;
p->next=q;
p=q;
}
ll ans=0;
f(i,0,n-2){
ans+=operate();
}
cout<<ans<<endl;
return 0;
}


看出来我该那里了么?

就是开始读入数据构造链表的时候。

代码TLE,直接就是输入一个插入一个,时间复杂度最坏是O((N^2)/2),AC代码,将这个构造链表的过程优化到了O(N*logN);

至于,主要的算法实现的过程,倒是没卡O((N^2)/2).

不清楚呀,为什么?

可能是,两个时间复杂度O(输入+插入)

第四步

学习别人代码:

优先队列的构造使用

《待补充》

STL中优先队列的使用

《待补充》

STL代码

/*STL 优先队列*/

//Memory Time
//512K   47MS

#include<iostream>
#include<vector>
#include<queue>
#include<cstdio>
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;
}


Finally

个人感觉,利用堆,才是AC这道题的最好办法。上述AC代码,过的并不能说服人,凑巧罢了。

利用堆,能将哈夫曼算法实现部分的时间复杂度优化到O(nlogn).

学习,如何构造堆,利用堆来实现哈夫曼树。

#include <iostream>
using namespace std;
long long n,i,ans,p[20001];
void heap(long x,long y)
{
long i,j,temp;
temp=p[x];
i=x;
j=i*2;
while(j<=y)
{
if(j<y&&p[j+1]<p[j])
j++;
if(temp>p[j])
{
p[i]=p[j];
i=j;
j=i*2;
}
else
break;
}
p[i]=temp;
}
int main()
{
cin>>n;
ans=0;
for(i=1;i<=n;i++)
cin>>p[i];
for(i=n/2;i>=1;i--)
heap(i,n);
while(n>1)
{
p[0]=p[1];
p[1]=p[n--];
heap(1,n);
p[1]+=p[0];
ans+=p[1];
heap(1,n);
}
cout<<ans<<endl;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: