题意:
给定M个数,每次可以插入序列一个数;再给N个数,表示在插入第几个数后输出一个数,第一次输出序列中最小的,第二次输出序列中第二小的……以此类推,直到输出N个数。
第一次做只用一个优先数列超时了,后来上网看到要用最大堆和最小堆维护做,网上的代码可以把数据都存储在最大堆中,每次加入新数据只要调动一两个就可以了,而我的代码每次加入新数据都得清空队列,相比较要慢多了。
要点:
先设两个优先队列,一个用来存储前k个数,从大到小排列;另一个存储k+1到i个数,从小到大排列,输出时只要输出第一个优先队列的堆顶就是第k大的数了。
每次拿到一个数,先判断第一个优先队列满不满k个,如果不满就直接放入第一个队列。
如果已经满足k个,就与第一个队列堆顶元素比较,如果这个数大于堆顶,说明第k大的数还是堆顶,就把这个数放进第二个队列,
如果这个数小于堆顶,说明第k大的数不是堆顶而是这个新数,那么就把堆顶放进第二个队列,把这个新数放进第一个队列,第一个队列当前的堆顶即第k大的数
总之保证第一个队列中任意一个都大于第二个队列
代码如下:
#include<cstdio>
#include<queue>
#include<functional> //要使用greater模板必须有这个头文件
using namespace std;
const int maxn = 30010;
int a[maxn], u[maxn];
int main()
{
int i, j, m, n, k, x, ans;
while (scanf("%d%d", &m, &n) != EOF)
{
priority_queue<int, vector<int>, less<int> >que1;//建立一个大根堆
priority_queue<int, vector<int>, greater<int> >que2;//建立一个小根堆
for (i = 1; i <= m; i++)
scanf("%d", &a[i]);
for (i = 1; i <= n; i++)
scanf("%d", &u[i]);
i = 0; //i代表a数组的下标
j = k = 1; //k代表第k大的数,j代表u数组的下标
while(j<=n)
{
if (i == u[j])//判断a数组是否放入u[j]个数
{
j++;
if (que1.size() < k)
{ //如果输入6,6,k变大而que1大小没变,就把que2的堆顶放进que1
x = que2.top();
que1.push(x);
que2.pop();
}
ans = que1.top(); //每次输出que1的堆顶就是第k大的数
printf("%d\n", ans);
k++; //每次弹出一个数,k就+1
}
else
{
i++;
//que1个数小于k,就先把a[i]压入que2,再把que2堆顶压入que1,保证是最小的放入que1
if (que1.size() < k)
{
que2.push(a[i]);
x = que2.top();
que2.pop(); //注意这里要去掉堆顶,因为它只是在que2走了个过场,最后还是要放到que1里
que1.push(x);
}
//如果que1堆顶大于新数,把que1堆顶放到que2中并把que1堆顶去掉,把a[i]放进que1,保证que1个数为1
else if (que1.top() > a[i])
{
x = que1.top();
que1.pop();
que1.push(a[i]);
que2.push(x);
}
else //que1堆顶小于a[i],第k大还是堆顶,直接把a[i]压入que2
que2.push(a[i]);
}
}
}
return 0;
}