2020年2月13日 OJ习题【优先队列】

首先来区分一下优先队列与队列不同的地方

定义

:priority_queue<type,vector < >,less(greater)< > >q;

(注意最后两个>中间要留有空格,否则编译器可能会认为是右移)
其中,type可以使int,long long等,但一般是自定义的结构体,也就是说优先队列常与结构体联系在一起;
需要注意的是,这里的排序与sort正好相反,less是从大到小排序,而greater是从小到大;如果第三个变量没有写,同时也没有自定义排序的话,默认是less即从大到小;

自定义排序写法

:struct pa

{

int t;

int id;

};

bool operator < (const pa &a,const pa &b)

这一句写法固定,记住就好了,缺一不可

{

if(a.t!=b.t) return a.t>b.t;

else return a.id>b.id;

}

这时候一般就是利用结构体,不过要记住,与sort的cmp排序也是恰好相反的;
还有就是,优先队列取队首元素是:

q.top();

合并果子-优先队列

基本的优先队列题

思路:先把果子都放在从小到大排序的优先队列里面,然后两个两个地找,再 把两个之和再放入队列中,直到找完即可。

#include <bits/stdc++.h>
using namespace std;

priority_queue<int,vector<int>,greater<int> >x;
int n,a[10005],sum=0,s1,s2;
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>a[i];
    for(int i=0;i<n;i++)
        x.push(a[i]);
    while(1)
    {
        s1=x.top();
        x.pop();
        s2=x.top();
        x.pop();
        sum+=(s1+s2);
        if(x.empty()) break;
        x.push(s1+s2);
    }
    cout<<sum<<endl;
    return 0;
}

合成陨石-优先队列

与上一题思路完全相同,只不过是多组输入

#include <bits/stdc++.h>
using namespace std;

priority_queue<int,vector<int>,greater<int> >x;
int n,a[10005],sum=0,s1,s2;
int main()
{
    while(cin>>n)
    {
        sum=0;
         for(int i=0;i<n;i++)
        cin>>a[i];
    for(int i=0;i<n;i++)
        x.push(a[i]);
    while(1)
    {
        s1=x.top();
        x.pop();
        s2=x.top();
        x.pop();
        sum+=(s1+s2);
        if(x.empty()) break;
        x.push(s1+s2);
    }
    cout<<sum<<endl;
    }
    return 0;
}

买饭-优先队列

思路还是按打饭时间升序排列,排在前面的打饭时间越短,后面的等待时间就越短,还有就是所有人的平均等待时间的求法:
**假设有N个人,那么最后一个人要等N-1个人的打饭时间,以此类推,最后会得到,第一个人一共被等了N-1次,第二个人被等了N-2次。。。。。。一直到倒数第二个人,所以平均等待时间就是:(a[1]*N-1+a[2]*N-2+…+a[N-1]1+a[N]0)/N;

#include <bits/stdc++.h>
using namespace std;

struct pa
{
    int t;//时间
    int id;//一开始的编号
};
bool operator < (const pa &a,const pa &b)
{
    if(a.t!=b.t) return a.t>b.t;//升序排列
    else return a.id>b.id;
}
priority_queue<pa,vector<pa> >x;
int n,a[1005];
int main()
{
    struct pa tmp;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        x.push({a[i],i});
    }
    int j=n-1;
    double sum=0;
    for(int i=1;i<n;i++)
    {
        tmp=x.top();
        x.pop();
        sum+=j*tmp.t;
        j--;
        cout<<tmp.id<<" ";
    }
    tmp=x.top();
    x.pop();
    cout<<tmp.id<<endl;
    printf("%.2lf\n",sum/n);
    return 0;
}

堆-优先队列

这个就简单了,就是把优先队列的入队,出队,清队首元素分步写出来

#include <bits/stdc++.h>
using namespace std;

priority_queue<int,vector<int>,greater<int> >x;
long long n,q,m;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>m;
        if(m==1)
        {
            cin>>q;
            x.push(q);
        }
        if(m==2)
            cout<<x.top()<<endl;
        if(m==3)
            x.pop();
    }
    return 0;
}

瑞瑞的木板-优先队列

这道题,一开始错了好几遍,原因在于还是没有理解透题意,瞄了一眼jwGG的题解才反应过来,这题的题意就是合并果子的逆过程,本质上是一样的;
我固定的认为切割木版时就是把想要长度的木板按降序一个个切下来就是最省的了,但是这是错误的,因为其实在过程中可以先把一块木板切割成两个长度都不是自己想要的木板,然后再分别对这两个木板进行切割,这样有时候最终才会是最省的,所以这题本质与合并果子是一样的

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
priority_queue<int,vector<int>,greater<int> >x;
int n,a[20005],s1,s2;
ll sum=0;//必须要long long才装得下
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
        x.push(a[i]);
    }
    while(x.size()>1)
    {
        s1=x.top();
        x.pop();
        s2=x.top();
        x.pop();
        sum+=(s1+s2);
        x.push(s1+s2);
    }
    cout<<sum<<endl;
    return 0;
}

接下来两道题就又是看了题解才理解要怎么写的了(明白题意,但是不会自己写代码。。。。。。)

序列合并-优先队列

以下是jwGG的思路:
首先,把A和B两个序列分别从小到大排序,变成两个有序队列。这样,从A和B中各任取一个数相加得到N^2个和,可以把这些和看成形成了n个有序表/队列:
A[1]+B[1] <= A[1]+B[2] <= … <= A[1]+B[N]
A[2]+B[1] <= A[2]+B[2] <= … <= A[2]+B[N]
……
A[N]+B[1] <= A[N]+B[2] <= … <= A[N]+B[N]
接下来,就相当于要将这N个有序队列进行合并排序:
首先,将这N个队列中的第一个元素放入一个优先队列中;
然后,每次取出堆中的最小值。若这个最小值来自于第k个队列,那么,就将第k个队列的下一个元素放入堆中。
时间复杂度:O(NlogN)。

关键是如何写才能实现上述操作,这就是我现在的短板了
可以定义成结构体,利用两个变量x,y来控制是哪两个数相乘

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct ad
{
    int x,y;
    ll sum;
};
bool operator < (const ad &a,const ad &b)
{
    return a.sum>b.sum;
}
priority_queue<ad,vector<ad> >q;
const int N=4e5+10;
int n,a[N],b[N];

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
        scanf("%d",&b[i]);
    for(int i=1;i<=n;i++)
        q.push({i,1,a[i]+b[1]});//先把a里面的数与b的第一个数的乘积都放入优先队列
    for(int i=1;i<=n;i++)
    {
        struct ad tmp=q.top();
        q.pop();
        printf("%d\n",tmp.sum);
        int x=tmp.x,y=tmp.y;
        q.push({x,y+1,a[x]+b[y+1]});//出队的是a中的那个数,就把这个数与b中对应的下一个数的乘积放入优先队列中
    }
    return 0;
}

桐桐的新闻系统-优先队列

这题思路与上一题相似

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct ad
{
    int id,t,sum;//t是原始间隔时间;sum是累计的时间,排序要按照这个
};
bool operator < (const ad &a,const ad &b)
{
    if(a.sum!=b.sum) return a.sum>b.sum;
    else return a.id>b.id;
}
priority_queue<ad,vector<ad> >q;
char str[10];
int k,id,t;

int main()
{
    ios::sync_with_stdio(false);
    while(cin>>str&&str[0]!='#')
    {
        cin>>id>>t;
        q.push({id,t,t});//一开始的sum就是t
    }
    cin>>k;
    for(int i=1;i<=k;i++)
    {
        struct ad tmp=q.top();
        q.pop();
        printf("%d\n",tmp.id);
        id=tmp.id;
        t=tmp.t;
        q.push({id,t,(t+tmp.sum)});//谁出队了,再入队时就要再加一个对应的原始间隔时间
    }
    return 0;
}

感谢jwGG!!!!!!果然我还是太垃圾了,还是消停儿跟着白嫖课程吧,进实验室太困难啦hhh

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值