堆分为小根堆和大根堆,为完全二叉树

小根堆是堆顶为最小的数,他的左右儿子都是要比他大,他的儿子们也可以构成一个小根堆

大根即为堆顶是最大的数,他的左右儿子都要比他小,他的儿子们也可以构成一个大根堆

可以看大佬博客:https://www.cnblogs.com/henry-1202/p/9307927.html

我们以洛谷3378为例 (小根堆)

题目描述

如题,初始小根堆为空,我们需要支持以下3种操作:

操作1: 1 x 表示将x插入到堆中

操作2: 2 输出该小根堆内的最小数

操作3: 3 删除该小根堆内的最小数

输入输出格式

输入格式:

 

第一行包含一个整数N,表示操作的个数

接下来N行,每行包含1个或2个正整数,表示三种操作,格式如下:

操作1: 1 x

操作2: 2

操作3: 3

 

输出格式:

 

包含若干行正整数,每行依次对应一个操作2的结果。

 

输入输出样例

输入样例#1: 复制

5
1 2
1 5
2
3
2

输出样例#1: 复制

2
5

说明

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=15

对于70%的数据:N<=10000

对于100%的数据:N<=1000000(注意是6个0。。。不过不要害怕,经过编者实测,堆是可以AC的)

样例说明:

故输出为2、5

AC代码如下

​
#include<bits/stdc++.h>
using namespace std;
int heap[1000005],size,x,y,n;
void push(int x)//插入 
{
	heap[++size]=x;//把这个数插入到堆底 
	long long  now=size;//从堆底开始 
	while(now>1)//堆里有俩个以上的数 
	{
		int next=now/2;//从他的爸爸开始 
		if(heap[now]<heap[next]) swap(heap[now],heap[next]);//如果他的爸爸比他大(即不符合最小堆的性质) 
		else break;//符合就结束 
		now=next;//从这个点再开始往上判断 
	}
}
void pop()//删除 
{
	swap(heap[size],heap[1]);//堆顶和堆底交换 
	size--;//直接把堆底删掉 
	int now=1;//从堆底开始往下 
	while(now*2<=size)//如果还可以往下走 
	{
		int next=now*2;//往下走 
		if(next+1<=size&&heap[next+1]<heap[next]) next++;//第一个条件判断他有没有右儿子  从俩个儿子里面选一个小儿子 
		if(heap[next]<heap[now]) swap(heap[next],heap[now]);//如果不符合最小堆的性质就交换 
		else break;//符合了就结束 
		now=next;//下一次判断从这里开始 
	}
} 
int main()
{
	cin>>n;
	for(int i=0;i<n;i++)
	{
		cin>>x;
		if(x==1)
		{
			cin>>y;
			push(y);
		}
		else if(x==2)
		{
			cout<<heap[1]<<endl;
		}
		else
		{
			pop();
		}
	}
	return 0;
}

​

STL版

#include<bits/stdc++.h>
using namespace std;
priority_queue<int,vector<int>,greater<int> > q;
int n,x,y;
int main()
{
	scanf("%d",&n);
	while(n--)
	{
		scanf("%d",&x);
		if(x==1)
		{
			scanf("%d",&y);
			q.push(y);
		}
		else if(x==2)
		{
			printf("%d\n",q.top());
		}
		else
		q.pop();
	}
	return 0;
}

洛谷P1801 黑匣子

题目描述

Black Box是一种原始的数据库。它可以储存一个整数数组,还有一个特别的变量i。最开始的时候Black Box是空的.而i等于0。这个Black Box要处理一串命令。

命令只有两种:

ADD(x):把x元素放进BlackBox;

GET:i加1,然后输出Blackhox中第i小的数。

记住:第i小的数,就是Black Box里的数的按从小到大的顺序排序后的第i个元素。例如:

我们来演示一下一个有11个命令的命令串。(如下图所示)

现在要求找出对于给定的命令串的最好的处理方法。ADD和GET命令分别最多200000个。现在用两个整数数组来表示命令串:

1.A(1),A(2),…A(M):一串将要被放进Black Box的元素。每个数都是绝对值不超过2000000000的整数,M$200000。例如上面的例子就是A=(3,1,一4,2,8,-1000,2)。

2.u(1),u(2),…u(N):表示第u(j)个元素被放进了Black Box里后就出现一个GET命令。例如上面的例子中u=(l,2,6,6)。输入数据不用判错。

输入输出格式

输入格式:

 

第一行,两个整数,M,N。

第二行,M个整数,表示A(l)

……A(M)。

第三行,N个整数,表示u(l)

…u(N)。

 

输出格式:

 

输出Black Box根据命令串所得出的输出串,一个数字一行。

 

输入输出样例

输入样例#1: 复制

7 4
3 1 -4 2 8 -1000 2
1 2 6 6

输出样例#1: 复制

3
3
1
2

说明

对于30%的数据,M≤10000;

对于50%的数据,M≤100000:

对于100%的数据,M≤200000。

利用一个大根堆一个小根堆来维护第k小

大根堆里存有第i-1小到最小的数   小根堆里存有第i小的数和剩下的数

#include<bits/stdc++.h>
using namespace std;
priority_queue<int,vector<int>,greater<int> >q;//小根堆 
priority_queue<int> q1;//大根堆 
int n,m,a[200010],u[200010];
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=m;i++) cin>>u[i];
	int j=1;//从第一个开始 
	for(int i=1;i<=m;i++)//i 表示要输出的是第几小 
	{
		for(;j<=u[i];j++)//j=1开始不断输入 
		{
			q1.push(a[j]);//把这个数放入大根堆里 让大根堆里的数永远只有i-1个
			if(q1.size()==i)//如果大根堆里的数等于i 
			{
				q.push(q1.top());//把这个数放入小根堆里 
				q1.pop();//出队 
			}// 小根堆里堆顶结束第i小的数 大根堆的堆顶是第i-1小的数(大根堆里存有第i-1小到最小的数) 
		}
		cout<<q.top()<<endl;//输出小根堆堆顶的数(即为第k小的数) 
		q1.push(q.top());//重新放回大根堆中 
		q.pop();//出队 
	}
	return 0;
}

洛谷P1168 中位数 

题目描述

给出一个长度为NN的非负整数序列A_iAi​,对于所有1 ≤ k ≤ (N + 1) / 21≤k≤(N+1)/2,输出A_1, A_3, …, A_{2k - 1}A1​,A3​,…,A2k−1​的中位数。即前1,3,5,…1,3,5,…个数的中位数。

输入输出格式

输入格式:

 

第11行为一个正整数NN,表示了序列长度。

第22行包含NN个非负整数A_i (A_i ≤ 10^9)Ai​(Ai​≤109)。

 

输出格式:

 

共(N + 1) / 2(N+1)/2行,第ii行为A_1, A_3, …, A_{2k - 1}A1​,A3​,…,A2k−1​的中位数。

 

输入输出样例

输入样例#1: 复制

7
1 3 5 7 9 11 6

输出样例#1: 复制

1
3
5
6

说明

对于20\%20%的数据,N ≤ 100N≤100;

对于40\%40%的数据,N ≤ 3000N≤3000;

对于100\%100%的数据,N ≤ 100000N≤100000。

#include<bits/stdc++.h>
using namespace std;
priority_queue<int>q;
priority_queue<int,vector<int>,greater<int> > Q;
int n,x[10000005];
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&x[i]);
	}
	int j=1;
	for(int i=1;i<=n;i+=2)
	{
		for(;j<=i;j++)
		{
			q.push(x[j]);
			if(q.size()==i/2+1)//大根堆里存一半 小根堆里存一半 
			{
				Q.push(q.top());
				q.pop();
			}
		}
		printf("%d\n",Q.top());
		q.push(Q.top());
		Q.pop();
	}
	return 0;
}

洛谷P2949 [USACO09OPEN]工作调度Work Scheduling

题意翻译

约翰有太多的工作要做。为了让农场高效运转,他必须靠他的工作赚钱,每项工作花一个单位时间。 他的工作日从0时刻开始,有10^9个单位时间。在任一时刻,他都可以选择编号1~N的N(1 <= N <= 10^6)项工作中的任意一项工作来完成。 因为他在每个单位时间里只能做一个工作,而每项工作又有一个截止日期,所以他很难有时间完成所有N个工作,虽然还是有可能。 对于第i个工作,有一个截止时间D_i(1 <= D_i <= 10^9),如果他可以完成这个工作,那么他可以获利P_i( 1<=P_i<=10^9 ). 在给定的工作利润和截止时间下,约翰能够获得的利润最大为多少.

题目描述

Farmer John has so very many jobs to do! In order to run the farm efficiently, he must make money on the jobs he does, each one of which takes just one time unit.

His work day starts at time 0 and has 1,000,000,000 time units (!). He currently can choose from any of N (1 <= N <= 100,000) jobs

conveniently numbered 1..N for work to do. It is possible but

extremely unlikely that he has time for all N jobs since he can only work on one job during any time unit and the deadlines tend to fall so that he can not perform all the tasks.

Job i has deadline D_i (1 <= D_i <= 1,000,000,000). If he finishes job i by then, he makes a profit of P_i (1 <= P_i <= 1,000,000,000).

What is the maximum total profit that FJ can earn from a given list of jobs and deadlines? The answer might not fit into a 32-bit integer.

输入输出格式

输入格式:

 

* Line 1: A single integer: N

* Lines 2..N+1: Line i+1 contains two space-separated integers: D_i and P_i

 

输出格式:

 

* Line 1: A single number on a line by itself that is the maximum possible profit FJ can earn.

 

输入输出样例

输入样例#1: 复制

3 
2 10 
1 5 
1 7 

输出样例#1: 复制

17 

说明

Complete job 3 (1,7) at time 1 and complete job 1 (2,10) at time 2 to maximize the earnings (7 + 10 -> 17).

代码:

#include<bits/stdc++.h>
using namespace std; 
long long  n,ans;
struct node
{
	long long  d,p;
	bool operator <(const  node&x)const 
	{
		return p>x.p;// 重载小于号使得小的先出队列   
	}
}a[100005];
bool cmp(node a,node b)//排序 
{
	if(a.d!=b.d)
	return a.d<b.d;
	return a.p<b.p;
}
priority_queue<node> q;
int main()
{
	scanf("%lld",&n);
	for(long long  i=1;i<=n;i++)
	scanf("%lld %lld",&a[i].d,&a[i].p);
	sort(a+1,a+n+1,cmp);
	ans=0;
	for(long long  i=1;i<=n;i++)
	{
		if(a[i].d<=q.size())//如果这个工作的截止时间小于等于队列的大小(即时间已经用完了,要么取代一个要么不要他) 
		{
			if(a[i].p>q.top().p)//如果这个工作的利润大于队列里面最小的利润就取代他 
			{
				ans+=a[i].p-q.top().p;
				q.pop();
				q.push(a[i]);
			}
		}
		else//时间还有的剩就直接加入 
		{
			ans+=a[i].p;
			q.push(a[i]);
		}
	}
	printf("%lld\n",ans);
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值