哈夫曼树(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; }
相关文章推荐
- 安卓开发 第一篇 关于依赖注入框架dagger2的使用和理解
- Xcode 技巧充电篇
- 产生死锁的原因和必要条件+解决死锁的基本方法
- 表面积最小(POJ3536)
- LeetCode *** 331. Verify Preorder Serialization of a Binary Tree
- 使用std::map和std::list存放数据,消耗内存比实际数据大得多
- 20145204实验二:面向对象设计
- 0414-复利计算器6.0.Release
- 复利计算器6.0
- 复利计算--结对2.0
- 关于c++中的模板
- string 对象读写
- Linux指令汇集
- Linux下的tar压缩解压缩命令详解
- unity3d热更新插件uLua学习整理
- iOS之SDWebImage清理缓存
- ABP源码分析三十一:ABP.AutoMapper
- 复制所有链接,全选,反选
- JavaScript之数组API、栈和队列、冒泡排序
- Datatables更新checkbox状态无效