3道经典的优先队列题(附优先队列的模板)

47 篇文章 0 订阅


优先队列是一种十分强大的数据结构,它保持了一种动态的有序性,对于不断改变有入队的操作,而又需要某种最大或最小的操作的问题是再合适不过了,通常优先队列的实现是由最小堆或者最大堆完成的,并通过堆排序保持队列的有序性,模拟队列的结构,在实际比赛中要写一个堆排序还是要一定的时间的,但是stl中queue容器中已经可以实现优先队列,下面以三道基本的题目来演示priority_queue的作用。

聪明的木匠  
Time Limit: 1000ms, Special Time Limit: 2500ms, Memory Limit: 32768KB  
Total submit users: 23, Accepted users: 10  
Problem 10611 : No special judgement  
Problem description  
    最近,一位老木匠遇到了一件非常棘手的问题。他需要将一根非常长的木棒切成N 段。每段的长度分别为 L1 ,L2 ,…,LN ( 1≤L1 ,L2 ,…,LN ≤1000 ,且均为整数)个长度单位。 ∑Li (i=1,2,…,N) 恰好就是原木棒的长度。我们认为切割时仅在整数点处切且没有木材损失。 
  木匠发现,每一次切割花费的体力与该木棒的长度成正比,不妨设切割长度为 1 的木棒花费 1 单位体力。例如,若 N=3 , L1 =3,L2 =4,L3 =5 ,则木棒原长为 12 ,木匠可以有多种切法,如:先将 12 切成 3+9. ,花费 12 体力,再将 9 切成 4+5 ,花费 9 体力,一共花费 21 体力;还可以先将 12 切成 4+8 ,花费 12 体力,再将 8 切成 3+5 ,花费 8 体力,一共花费 20 体力。显然,后者比前者更省体力。 
  那么,木匠至少要花费多少体力才能完成切割任务呢? 

 
Input  
  输入数据的第一行为一个整数N(2≤N≤150,000) ; 
在接下来的 N 行中,每行为一个整数 Li (1≤Li ≤1000) 。 

 
Output  
  输出数据仅有一行,为一个整数,表示木匠最少要花费的体力。测试数据保证这个整数不大于231 -1 。 

 
Sample Input  
4
3
5
7
11
 
Sample Output  
49 
Problem Source  
  2010年河北大学程序设计竞赛 
 

 

最优的方法是每次切出最小的合并,显然要使用优先队列;

[cpp:firstline[1]] view plaincopyprint?#include <iostream>   
#include <queue>   
struct cmp  
{  
    bool operator ()(const int &i,const int &j)  
    {  
        return i>j;  
    }  
};  
using namespace std;  
int main()  
{  
    priority_queue<int,vector<int>,cmp> s;  
    int n;  
    while(cin>>n)  
    {  
        for(int i=0;i<n;i++)  
        {  
            int key;  
            scanf("%d",&key);  
            s.push(key);  
        }  
        int sum=0;  
        while(n>=2)  
        {  
            int a,b;  
            a=s.top();  
            s.pop();  
            b=s.top();  
            s.pop();  
            s.push(a+b);  
            //cout<<a<<' '<<b;   
            sum+=a+b;  
            n--;  
        }  
        cout<<sum<<endl;  
    }  
    return 0;  
      
}  
#include <iostream>
#include <queue>
struct cmp
{
    bool operator ()(const int &i,const int &j)
    {
        return i>j;
    }
};
using namespace std;
int main()
{
    priority_queue<int,vector<int>,cmp> s;
    int n;
    while(cin>>n)
    {
        for(int i=0;i<n;i++)
        {
            int key;
            scanf("%d",&key);
            s.push(key);
        }
        int sum=0;
        while(n>=2)
        {
            int a,b;
            a=s.top();
            s.pop();
            b=s.top();
            s.pop();
            s.push(a+b);
            //cout<<a<<' '<<b;
            sum+=a+b;
            n--;
        }
        cout<<sum<<endl;
    }
    return 0;
    
}
 

 

【问题描述】
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有3种果子,数目依次为1,2,9。可以先将 1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为 12。所以多多总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。
【输入文件】
输入文件fruit.in包括两行,第一行是一个整数n(1 <= n <= 10000),表示果子的种类数。第二行包含n个整数,用空格分隔,第i个整数ai(1 <= ai <= 20000)是第i种果子的数目。
【输出文件】
输出文件fruit.out包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这个值小于231。
【样例输入】
3
1 2 9
【样例输出】
15
【数据规模】
对于30%的数据,保证有n <= 1000;
对于50%的数据,保证有n <= 5000;
对于全部的数据,保证有n <= 10000。 

 

合并的最优方法是合并最小的两堆,通过优先队列就可以轻松实现;

[cpp:firstline[1]] view plaincopyprint?#include <iostream>   
#include <queue>   
struct cmp  
{  
    bool operator ()(const int &i,const int &j)  
    {  
        return i>j;  
    }  
};  
using namespace std;  
int main()  
{  
    priority_queue<int,vector<int>,cmp> s;  
    int n;  
    while(cin>>n)  
    {  
        for(int i=0;i<n;i++)  
        {  
            int key;  
            scanf("%d",&key);  
            s.push(key);  
        }  
        int sum=0;  
        while(n>=2)  
        {  
            int a,b;  
            a=s.top();  
            s.pop();  
            b=s.top();  
            s.pop();  
            s.push(a+b);  
            //cout<<a<<' '<<b;   
            sum+=a+b;  
            n--;  
        }  
        cout<<sum<<endl;  
    }  
    return 0;  
      
}  
#include <iostream>
#include <queue>
struct cmp
{
    bool operator ()(const int &i,const int &j)
    {
        return i>j;
    }
};
using namespace std;
int main()
{
    priority_queue<int,vector<int>,cmp> s;
    int n;
    while(cin>>n)
    {
        for(int i=0;i<n;i++)
        {
            int key;
            scanf("%d",&key);
            s.push(key);
        }
        int sum=0;
        while(n>=2)
        {
            int a,b;
            a=s.top();
            s.pop();
            b=s.top();
            s.pop();
            s.push(a+b);
            //cout<<a<<' '<<b;
            sum+=a+b;
            n--;
        }
        cout<<sum<<endl;
    }
    return 0;
    
}
 

 

删除最小值  
Time Limit: 1000ms, Special Time Limit: 2500ms, Memory Limit: 32768KB  
Total submit users: 8, Accepted users: 8  
Problem 10604 : No special judgement  
Problem description  
  序列A 中有 N 个元素,序列 B 中也有 N 个元素,依次将序列 B 中的元素移进序列 A ,对于每次移进元素,首先在一行输出序列 A 中的最小值,然后在 A 在加入该元素,接着在序列 B 中删除该元素,接着在序列 A 中删除刚才输出的最小值。

 
Input  
  共三行,第一行给出元素数目N ,第二行给出 A 中的原始 N 个元素,第三行给出 B 中的原始 N 个元素。 1 ≤ n ≤ 10000 ,每个元素值范围为 [0 , 100000] 。

 
Output  
  在每次将B中的元素移入A之前,在一行里输出A中所有元素的最小值。

 
Sample Input  
3
3 9 6
5 2 10
 
Sample Output  
3
5
2
 

这道题目跟到模拟题目差不多……

[cpp:firstline[1]] view plaincopyprint?#include <iostream>   
#include <queue>   
struct cmp  
{  
    bool operator ()(const int &i,const int &j)  
    {  
        return i>j;  
    }  
};  
using namespace std;  
int main()  
{  
    int b[10003];  
    priority_queue<int,vector<int>,cmp> s;  
    int n;  
    while(cin>>n)  
    {  
        for(int i=0;i<n;i++)  
        {  
            int key;  
            scanf("%d",&key);  
            s.push(key);  
              
        }  
        for(int i=0;i<n;i++)  
        {  
            scanf("%d",&b[i]);  
        }  
        for(int j=0;j<n;j++)  
        {  
              
            //cout<<b[j]<<endl;   
            printf("%d/n",s.top());  
            s.pop();  
            s.push(b[j]);  
              
        }  
          
     }  
    return 0;  
      
}  
#include <iostream>
#include <queue>
struct cmp
{
    bool operator ()(const int &i,const int &j)
    {
        return i>j;
    }
};
using namespace std;
int main()
{
    int b[10003];
    priority_queue<int,vector<int>,cmp> s;
    int n;
    while(cin>>n)
    {
        for(int i=0;i<n;i++)
        {
            int key;
            scanf("%d",&key);
            s.push(key);
            
        }
        for(int i=0;i<n;i++)
        {
            scanf("%d",&b[i]);
        }
        for(int j=0;j<n;j++)
        {
            
            //cout<<b[j]<<endl;
            printf("%d/n",s.top());
            s.pop();
            s.push(b[j]);
            
        }
        
     }
    return 0;
    
}
 

以上三道题目如果掌握优先队列再去解答十分简单,做这种优先队列的题目,如果发现是使用优先队列的就很简单了(说的这都是废话!)

stl中的优先队列的模板:

#include <queue>

priority_queue <int> q;

这是按照从大到小的队列;

#include <queue>

struct cmp

{

    bool operator ()(const int &i,const int &j)

    {

        return i>j;

    }

};

priority_queue <int> q;

这是从小到大的优先队列;


转自:http://blog.csdn.net/south__wind/article/details/7929785

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值