电子学会七级-数据结构-堆和优先队列

堆的基本存储
https://www.runoob.com/data-structures/heap-storage.html
堆的 shift up
https://www.runoob.com/data-structures/heap-shift-up.html
堆的 shift down
https://www.runoob.com/data-structures/heap-shift-down.html
基础堆排序
https://www.runoob.com/data-structures/heap-sort.html

堆排序
https://www.bilibili.com/video/BV1Eb41147dK?spm_id_from=333.337.search-card.all.click
实现堆
https://www.bilibili.com/video/BV1V44y1j7bJ?spm_id_from=333.337.search-card.all.click&vd_source=29bdf11ae30b7aaca85ec74dc1b8e1ad

【模板】堆
https://www.luogu.com.cn/problem/P3378

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

const int MAXN = 1e6 + 10;
int n, heap[MAXN], len;

//删除一个节点后 调整最后一个节点到根节点 向下重新构建堆 
void shiftDown(int heap[], int start, int end){
    // parent为欲调整结点, child 为其左孩子  child+1右孩子 
	int parent = start;
	int child = parent * 2;

	while (child <= end)  { // 存在孩子节点
		if (child + 1 <= end && heap[child] > heap[child + 1])//如果右孩子小 调整右孩子 
				child++;

		if (heap[parent] > heap[child]) {//parent>child  需要把小的调整到父节点 小根堆 
			swap(heap[parent], heap[child]);// parent child对换 
			parent = child;//有影响的子节点  设置parent 继续向下调整 
			child = parent * 2;//继续找左孩子 
		} else//不需要调整 
			return;
	}
}
//在最后追加一个节点,向上重新构建有影响的堆 对应的节点 
void shiftUp(int heap[], int start, int end){
	int child = end, parent = child / 2;//child=end 从最后开始调整    找到child 对应 parent 
	while(parent >= start) {//parent 在需要调整范围内 
		if(heap[parent] > heap[child]) {//parent > child 调整 小根堆   
			swap(heap[parent], heap[child]);//对换child 和parent 另一孩子和父节点数据不变 不需要管另一的孩子 
			child = parent;//child 变成parent 继续向上调整 
			parent = child / 2;//找父节点 
		} else {//不需要调整 
			return;
		}
	}
}

void insert(int heap[], int x){//数组最后插入一个元素后 向上调整有影响的节点 
	len++;
	heap[len] = x;//让元素个数+1,然后将数组末尾赋为x
	shiftUp(heap, 1, len);//调整有影响的节点 
}

void del(int heap[]){//删除根节点后 把最后一个元素替换到根节点 向下调整有影响的节点 
	swap(heap[1], heap[len]);//最后一个元素替换第一个元素-根 
	len--;//数组数量-1 
	shiftDown(heap, 1, len);//向下调整有影响的节点 
}

int main(){
    scanf("%d", &n);
    int op, x;
	for(int i = 1; i <= n; i++) {
		scanf("%d", &op);
		if(op == 1){
			scanf("%d", &x);
			insert(heap, x);
		} else if (op == 2) {
			printf("%d\n", heap[1]);
		} else if(op == 3) {
			del(heap);
		}
	}
	return 0;
}

【模板】快速排序
https://www.luogu.com.cn/problem/P1177
合并果子
https://www.luogu.com.cn/problem/P1090

#include<bits/stdc++.h> 
using namespace std;
typedef long long ll;
int main()
{
    priority_queue <ll, vector<ll>, greater<ll> > p;
    int n;
    cin >> n;
    ll ans = 0;
    while (n--)
    {
        ll a;
        cin >> a;
        p.push(a);
    }
    while (p.size() >= 2)
    {
        ll temp = 0;
        ll a;
        a = p.top();
        ans += a;
        temp += a;
        p.pop();
        a = p.top();
        ans += a;
        temp += a;
        p.pop();
        p.push(temp);
    }
    cout << ans << endl;
    return 0;
}

瑞瑞的木板
https://www.luogu.com.cn/problem/P1334

//可以逆向思维  假设已经切好 每次选最小的合并 类似合并果子 
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
ll ans;
priority_queue<ll,vector<ll>,greater<ll> > q;//优先队列  小根堆 
int main(){
	cin>>n;
	for(int i=0;i<n;i++){
		int num;
		cin>>num;
		q.push(num);//所有放入优先队列 
	}
	
	while(q.size()>=2){//只有队列里面有两个 则出队 
		ll a=q.top();//获取一个  最小 
		q.pop();
		ll b=q.top();//再获取一个 第二小 
		q.pop(); 
		ans+=a+b;// 最小 + 第二小 
		q.push(a+b);//和放回队列 参与下次计算 
	}
	cout<<ans;//输出则是切分需要最少能量 
}

序列合并
https://www.luogu.com.cn/problem/P1631

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int N,A[maxn],B[maxn],from[maxn];
priority_queue< pair<int,int>,vector< pair<int,int> >,greater< pair<int,int> > > q;
int main(){
	cin>>N;
	for(int i=1;i<=N;i++){
		cin>>A[i];
	}
	
	for(int i=1;i<=N;i++){
		cin>>B[i];
		from[i]=1;//A[i]先与B[1]相加  记录A[i]和 加到B的第几个 
		q.push(pair<int,int>(A[i]+B[from[i]],i));//相加和 A的下标 
	}
	
	while(N--){
		cout<<q.top().first<<" ";
		int pos=q.top().second;
		q.pop();
		from[pos]++;//A[pos]对应B的下一个 放入队列 参与比较 
		q.push(pair<int,int>(A[pos]+B[from[pos]],pos));
	} 
}

【深基16.例1】淘汰赛
https://www.luogu.com.cn/problem/P4715


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

const int N = 7;
int n;

struct Node {
	int country, power;
} t[2 << N + 10];

void build() {  // build the tree 两个队产生一个胜利对继续比赛 
	for (int i = n - 1; i > 0; --i) {
		int left = i << 1, right = i << 1 | 1;//left 左子树  right右子树 i<<1|1 =i*2+1 

		if(t[left].power < t[right].power){
			t[i].power =  t[right].power;
			t[i].country = t[right].country;
		} else {
			t[i].power =  t[left].power;
			t[i].country = t[left].country;
		}
	}
}

int main() {
	scanf("%d", &n);
	n = 1 << n;
	for (int i = 0; i < n; ++i) {
		scanf("%d", &t[n+i].power);//读入能力值 
		t[n+i].country = i + 1;//给国家编号 从1开始 
	}
	build();//构建比赛树 
	if(t[1].power == t[2].power)// 每个国家能力值不同 且 1==1 说明 1和2是一个国家 
		printf("%d\n", t[3].country);//亚军是第三个国家 
	else
		printf("%d\n", t[2].country);//1 2不是一个国家 亚军是2 

	return 0;
}

中位数
https://www.luogu.com.cn/problem/P1168

舞蹈课
https://www.luogu.com.cn/problem/P1878

#include<bits/stdc++.h>
using namespace std;
const int maxn=2*1e5+5;
struct node{
	int x,y,z;//两位舞者  x y  技术差 z 
	node(int xx,int yy,int zz){//构造函数 
		x=xx;
		y=yy;
		z=zz;
	}
// node2为堆顶元素 node1>node2为小顶堆
//https://blog.csdn.net/qq_44144025/article/details/119355668
	friend bool operator < (node node1,node node2){//构造小顶堆 
		if(node1.z!=node2.z){// 技术差不同 按技术差构造小顶堆 
			return node1.z>node2.z;
		}
		return node1.x>node2.x;//技术差相同 按第一个编号构造小顶堆 
	}
};
priority_queue<node,vector<node>> pqueue;//使用vector构造小顶堆 
string s;
int n,q[maxn],idx;//n个舞蹈者 q[]技术值 idx累加ans数组下标 
bool f[maxn],vis[maxn];//f[] 男女 true 男 false女 vis是否已跳过舞 
pair<int,int> ans[maxn]; //结对跳舞 
int main(){
	cin>>n;//跳舞人数 
	cin>>s;//男女字符串 
	for(int i=0;i<n;i++){//使用f数组把男女分开 
		if(s[i]=='B'){
			f[i+1]=true;
		}else{
			f[i+1]=false;
		}
	}
	
	for(int i=1;i<=n;i++){//输入技术值 
		cin>>q[i];
	}
	
	for(int i=1;i<n;i++){
		if(f[i]!=f[i+1]){//异性 相邻是异性 放入队列 等待尝试跳舞 
			pqueue.push(node(i,i+1,abs(q[i]-q[i+1]))); 
		}
	}
	
	while(!pqueue.empty()){//队列不为空 处理跳舞顺序 
		int x = pqueue.top().x;
		int y = pqueue.top().y;
		pqueue.pop();//取出技术值差小的 一对舞者 x y 
		if(!vis[x] && !vis[y]){//没跳过 可以去跳 
			vis[x]=true;//标识x已经跳过,后续不再跳 
			vis[y]=true;//标识y已经跳过,后续不再跳
			ans[++idx].first=x;
			ans[idx].second=y;//跳过的x y记录到ans数组 
			while(x>0 && vis[x]){//当前这一对 前一个和后一个是否可以组合一对舞者 
				x--;
			}
			while(y<n && vis[y]){//后一个没有跳过的 
				y++;
			}
			if(x > 0 && y <= n && f[x]!=f[y]){//如果是异性 可以组合一对舞者 放入队列尝试去跳 
				pqueue.push(node(x,y,abs(q[x]-q[y])));
			}
		}
	}
	
	cout<<idx<<endl;//跳舞对数 
	for(int i=1;i<=idx;i++){//每一对两个成员 
		cout<<ans[i].first<<" "<<ans[i].second<<endl;
	}
	return 0;
}

[NOI2015] 荷马史诗
https://www.luogu.com.cn/problem/P2168

#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>

using namespace std;

typedef long long LL;
typedef pair<LL, int> PLI;//pair两个数组合成一个数 

const int N = 1e5+10;

int n, m;
//使用vector存储 PLI的小顶堆
//pair构造的优先队列 默认按第一个树排序 如果第一个相等按第二个树排序 
priority_queue<PLI,vector<PLI>,greater<PLI>> heap; 

int main(){
    cin>>n>>m;//n种单词 使用m进制
    for (int i=0;i<n;i++){
        LL w;
        cin>>w;//每种单词出现次数 
        heap.push({w, 0});
    }
	//每次合并一层k-1个数 即: k个数变成1个数 合并了k-1个数
	//如果最后恰好为k,则可以合并  因此如果n不够 则最后一层补0 
    while ((n - 1) % (m - 1)) { 
        heap.push({0ll, 0});
        n++;
    }

    LL res = 0;
    while (heap.size() > 1) {//优先队列不为空 则合并处理 
        LL sum = 0;//累加本层总数 
        int depth = 0;//k叉树深度 
        for (int i = 0; i < m; i++) {//从底向上合并
            sum += heap.top().first;//取当前出现次数 
            depth = max(depth, heap.top().second);//获取高度 赋值depth 
            heap.pop();//弹出 
        }
        res += sum;//累加编码长度 
        heap.push({sum, depth + 1});//合并后深度加1 
    }

    cout << res << endl;//编码最短长度 
    cout << heap.top().second << endl;//最深叶子节点的深度 
    return 0;
}

黑匣子
https://www.luogu.com.cn/problem/P1801
[HNOI2003]操作系统
https://www.luogu.com.cn/problem/P2278

偏数学
最小函数值
https://www.luogu.com.cn/problem/P2085

posted @ 2022-04-16 20:42  new-code  阅读(25)  评论(0编辑  收藏  举报