先看一道题目:
STA的分裂
题目描述
不知道为什么,STA的生活变得异常忙碌,每天起来就发现自己有n件工作要做。于是STA只好把他们都记下来,拉成一个待办清单。
白天STA最多也只能做m次操作,要么就是从待办清单上挑一件工作完成它,要么就是把一件新的工作加到清单里面。
晚上STA还要处理白天没处理完的工作,这样才能上床睡觉。
为了提高自己的效率,STA决定给这些工作各自都标上一个优先级p。为了好区分,每件工作的优先级都不会重复。因此从待办清单上选择工作的时候,STA总是会选优先级最大的工作。
可惜STA一天要做的工作太多了,他想了想,索性写个程序帮他管理这些。
输入格式
第一行两个整数n,m 下面n行整数为n件工作各自的优先级p 在下面为m行操作的指令,共分为两种
第一种
q
表示选取当前清单里面优先级最高的工作
第二种
a 10
表示将优先级为10的工作加入待办清单
如果在m次操作结束后,待办清单不为空,那么STA将会不停从里面选取优先级最大的工作完成它,直到清单为空。
输出格式
每次STA从待办清单上选择了一项工作去完成,就产生一行输出,包含这件工作的优先级。
输入样例
5 6
1
2
5
9
7
q
q
a 8
q
a 4
q
输出样例
9
7
8
5
4
2
1
数据范围
10≤n≤1e6,n+1≤m≤1.5×n,1≤p≤1e9
堆的基本操作(以最大堆为例)
创建堆
例如将数组h[100]中的元素建立一个堆,只需要从最后一个节点往上一次进行向下调整就可以了
void create()//建堆
{
int i;
for(i=n;i>=1;i--) down(i);
}
向上调整
将一个子节点与他的父节点比较,把小的节点向上移
代码:
void up(int i)
{
int flag=0;
if(i==1) return; //如果是堆顶,就不需要调整了
while(i!=1 && flag==0)
{
//判断是否比父节点大
if(h[i]>h[i/2]) swap(i,i/2);//交换他和他爸爸的位置
else flag=1;//表示不需要调整了,当前节点值比父节点大
i=i/2;//更新i为父节点的编号,方便下次向上调整
}
}
向下调整
将一个父节点与他的左右节点比较,把大的往下移
代码:
void down(int i)
{
int t,flag=0; //flag用来标记是否需要继续向下调整
//当i节点有儿子(至少有左儿子)并且需要继续调整时,循环就执行
while(flag==0 && 2*i<=n )
{
//先判断i节点与左儿子的关系,用t记录值较大的节点编号
if( h[i] < h[i*2]) t=i*2;
else t=i;
//如果有右儿子,再对右儿子进行讨论
if(i*2+1<=n)
{
if(h[t] < h[i*2+1]) t=i*2+1;
}
//如果发现最大节点编号不是自己,说明子节点中有更大的
if(t!=i)
{
swap(t,i);
i=t;//更新i为刚才与它交换的儿子节点的编号,方便继续向下调整
}
else flag=1;//不需要调整
}
}
题目解答完整代码:
`#include<stdio.h>
int n,m,h[1000005];
void swap(int x,int y)
{
int t;
t=h[x];h[x]=h[y];h[y]=t;
}
void down(int i)
{
int t,flag=0; //flag用来标记是否需要继续向下调整
//当i节点有儿子(至少有左儿子)并且需要继续调整时,循环就执行
while(flag==0 && 2*i<=n )
{
//先判断i节点与左儿子的关系,用t记录值较大的节点编号
if( h[i] < h[i*2]) t=i*2;
else t=i;
//如果有右儿子,再对右儿子进行讨论
if(i*2+1<=n)
{
if(h[t] < h[i*2+1]) t=i*2+1;
}
//如果发现最大节点编号不是自己,说明子节点中有更大的
if(t!=i)
{
swap(t,i);
i=t;//更新i为刚才与它交换的儿子节点的编号,方便继续向下调整
}
else flag=1;//不需要调整
}
}
void up(int i)
{
int flag=0;
if(i==1) return; //如果是堆顶,就不需要调整了
while(i!=1 && flag==0)
{
//判断是否比父节点大
if(h[i]>h[i/2]) swap(i,i/2);//交换他和他爸爸的位置
else flag=1;//表示不需要调整了,当前节点值比父节点大
i=i/2;//更新i为父节点的编号,方便下次向上调整
}
}
int deletemax()
{
int t=h[1];//记录堆顶点的值
h[1]=h[n];//将堆的最后一个点赋值到堆顶
n--;//堆的元素减少1
down(1);//向下调整
return t;
}
void create()//建堆
{
int i;
for(i=n;i>=1;i--) down(i);
}
int main()
{
scanf("%d%d",&n,&m);
int i;
for(i=1;i<=n;i++)
scanf("%d",&h[i]);
create();
for(i=0;i<m;i++)
{
getchar();
char s;int k;
scanf("%c",&s);
if(s=='q')
{
printf("%d\n",deletemax());
}
else if(s=='a')
{
scanf("%d",&k);
n++;
h[n]=k;
up(n);
}
int p;
// printf("现在堆中的元素有:");
//for(p=1;p<=n;p++) printf("%d ",h[p]);
//printf("\n");
}
while(n>=1) printf("%d\n",deletemax());