首页 > 其他分享 >P3378 【模板】堆

P3378 【模板】堆

时间:2023-03-08 23:55:28浏览次数:36  
标签:include P3378 int next heap now 模板 size

P3378 【模板】堆

【模板】堆

题目描述

给定一个数列,初始为空,请支持下面三种操作:

  1. 给定一个整数 x,请将 x 加入到数列中。
  2. 输出数列中最小的数。
  3. 删除数列中最小的数(如果有多个数最小,只删除 1 个)。

输入格式

第一行是一个整数,表示操作的次数 n。
接下来 n 行,每行表示一次操作。每行首先有一个整数 op 表示操作类型。

  • 若 op = 1,则后面有一个整数 x,表示要将 x 加入数列。
  • 若 op = 2,则表示要求输出数列中的最小数。
  • 若 op = 3,则表示删除数列中的最小数。如果有多个数最小,只删除 1 个。

输出格式

对于每个操作 2,输出一行一个整数表示答案。

样例 #1

样例输入 #1

5
1 2
1 5
2
3
2

样例输出 #1

2
5

提示

【数据规模与约定】

  • 对于 30% 的数据,保证 n ≤ 15。
  • 对于 70% 的数据,保证 n ≤ 10^4。
  • 对于 100% 的数据,保证 1 ≤ n ≤ 10^6,1 ≤ x < 2^31,op ∈{1, 2, 3}。

分析

手写一个堆(原版)

向堆中添加元素的算法(put)如下:(小根堆)

1.在堆尾加入这个元素,并把它标志为当前节点

2.比较当前节点a(下标i)和它父节点b(下标i/2)的大小

{ 1.如果a<b,则swap(a,b),并把a标志为当前节点,继续.

2.如果a>=b,则退出

} 重复进行n次put操作,便可建一个小根堆

附:下标关系是完全二叉树的性质。

具体来说是读入一个数插入到堆尾,在通过调整以满足堆(这里是小根堆)的性质。

从now=len(堆的大小)开始,判断它于父节点(now>>1/now/2)的大小

若heap[now]<heap[now/2] 则swap(heap[now],heap[now/2]),且now=now/2,继续判断heap[now]于heap[now/2]的大小......

直到now=1或heap[now]>=heap[now/2]为止

代码就是这样

  void put(int d)
{
 int now,next;
 heap[++heap_size]=d;
 now=heap_size;
 while(now>1)
 {
  next=now>>1;
  if(heap[now]>=heap[next]) return;
  swap(heap[now],heap[next]);
  now=next;           
 }    
}

从堆中取出并删除这个元素的算法(get)如下:(小根堆)

1.取出堆的根节点的值

2.把堆的最后一个节点(heap_size)放到根上,覆盖掉根,heap_size--.

3.调整调整以满足堆(这里是小根堆)的性质。(put中调整的逆过程)

代码如下

 int get()
{
 int now,next,res;
 res=heap[1];
 heap[1]=heap[heap_size--];//注意是heap_size--,而不是--heap_size
 now=1;
 while(now*2<=heap_size)
 {
  next=now*2;
  if(next<heap_size&&heap[next+1]<heap[next]) next++;
  if(heap[now]<=heap[next]) return res;
  swap(heap[now],heap[next]);
  now=next;                      
 }
 return res;   
}

本题是3个操作,其实就是把get分成了两个.(取而不删除,删除了不取) 完整代码:

#include"cstdio"
#include"iostream"
#include"cctype"
using namespace std;
int heap_size,n;
int heap[30001];
inline int readint()
{
 char c=getchar();
 while(!isdigit(c)) c=getchar();
 int x=0;
 while(isdigit(c))
 {
  x=x*10+c-'0';
  c=getchar();                
 }
 return x;      
}
int buf[105];  
inline void writeint(int i) {  
    int p = 0;  
    if(i == 0) p++;  
    else while(i) {  
        buf[p++] = i % 10;  
        i /= 10;  
    }  
    for(int j = p-1; j >= 0; j--) putchar('0' + buf[j]);  
}  
void swap(int &a,int &b)
{
 int t=a;a=b;b=t;    
}
void put(int d)
{
 int now,next;
 heap[++heap_size]=d;
 now=heap_size;
 while(now>1)
 {
  next=now>>1;
  if(heap[now]>=heap[next]) return;
  swap(heap[now],heap[next]);
  now=next;           
 }    
}
int del()
{
 int now,next,res;
 res=heap[1];
 heap[1]=heap[heap_size--];
 now=1;
 while(now*2<=heap_size)
 {
  next=now*2;
  if(next<heap_size&&heap[next+1]<heap[next]) next++;
  if(heap[now]<=heap[next]) return res;
  swap(heap[now],heap[next]);
  now=next;                      
 } 
}
int get()
{
 return heap[1];   
} 
void work()
{
 n=readint();
 for(int i=1;i<=n;i++)
 {
  int x;
  x=readint();
  if(x==1) {x=readint();put(x);}
  else if(x==2) {writeint(get());putchar('\n');}
  else del();       
 }
}
int main()
{
 work();   
}

STL版堆(push_heap,pop_heap)

#include"cstdio"
#include"iostream"
#include"algorithm"
using namespace std;
int heap_size;
int heap[1000005];
void put(int x)
{
 heap[++heap_size]=x;
 push_heap(heap+1,heap+1+heap_size,greater<int>());  
}
int get()
{    
 return heap[1];   
}
void del()
{ 
 pop_heap(heap+1,heap+1+heap_size,greater<int>());  
 heap_size--;    
}
int main()
{
 int n;
 scanf("%d",&n);
 int x;
 for(int i=1;i<=n;i++)
 {
  scanf("%d",&x);
  if(x==1) {scanf("%d",&x);put(x);}
  else if(x==2) {printf("%d\n",get());}
  else if(x==3) del();      
 }
}

小根堆其实就是特殊的优先队列(越小的整数优先级越大)

#include"iostream"
#include"algorithm"
#include"queue"
using namespace std; 
priority_queue<int,vector<int>,greater<int> >q;
int main()
{
 int n;
 scanf("%d",&n);
 for(int i=1;i<=n;i++)
 {
  int x;
  scanf("%d",&x);
  if(x==1) {scanf("%d",&x);q.push(x);}
  else if(x==2) printf("%d\n",q.top());
  else q.pop();       
 }   
} 

PS:也可以直接用priority_queue q,但加入时把它的相反数入优先队列......来改变优先顺序 然后一堆负操作

我只想说写priority_queue<int,vector,greater >q不是更好吗?

标签:include,P3378,int,next,heap,now,模板,size
From: https://www.cnblogs.com/bujidao1128/p/17196753.html

相关文章

  • 设计模式(十七)----行为型模式之模板方法模式
    行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为......
  • 上传数据、下载模板文件解决方案(前端:antd;后端:.Net Core WebAPI)
    上传数据、下载模板文件解决方案(前端:antd;后端:.NetCoreWebAPI) 阅读目录一、Excel模板下载二、上传Excel表格三、.NetCore3.0WebAPI文件接收与解析 ......
  • 高精度模板2--zhengjun
    只支持非负数,用vectoer实现。#include<bits/stdc++.h>usingnamespacestd;usingll=unsignedlonglong;usingbigint=unsignedlonglong;usingbigbig=__int128;......
  • 软著申请模板,帮助了不少小伙伴少走弯路
    大家好,我是小悟说话题之前,先分享一件对我来说值得分享的事情,那就是又有一张软件著作权证书申请通过了,目前是待发放的状态,也就是说材料已经审核通过,等待发放邮寄出去了。话说......
  • 我的 poly 模板
    #include<bits/stdc++.h>#include<assert.h>usingnamespacestd;typedeflonglongll;typedefdoubledb;#defineepemplace_back#definepiipair<int,int>#d......
  • 欧拉函数模板
    //求n的欧拉函数intcalPhi(intn){intret=n;intbd=std::sqrt(n);for(inti=2;i<=n/i;++i){if(n%i==0){......
  • 织梦显示模板的PHP代码
    require_once("../include/common.inc.php");require(dirname(__FILE__)."/config.php");require_onceDEDEINC."/arc.partview.class.php";$pv=newPartView();......
  • Python Flask 之 路由和渲染模板讲解与示例演示
    目录一、概述二、路由三、渲染模板四、重定向和错误五、日志六、集成WSGI中间件一、概述Flask是一款使用Python编写的Web应用框架,其设计理念是轻量级和简单易学。......
  • c++ 模板的简单使用
    c++函数模板的两种用法,第二种是可变参数个数的使用方法,其中sizeof...()函数可以获取输入可变参数的数量#include<iostream>template<typenameT>TAddMyNum(const......
  • 树链剖分模板(为什么我的这么长???)
    #include<iostream>#include<cstdio>#include<cmath>#include<algorithm>#include<cstring>#definelidid<<1#defineridid<<1|1usingnamespacestd;l......