洛谷 P1631 序列合并 题解

P1631 序列合并

铺垫

二叉堆

概念:树形结构,父节点是所有节点中的最值

典型应用:

  • 堆排序
  • 优先队列

二叉堆支持:对最值的插入、删除、查询

本质:一棵满足“堆性质”的完全二叉树

根据完全二叉树的性质,采用层次序列的存储方法,用数组来保存二叉堆。有以下性质:

  • i > 1 的节点,其父节点位于 i / 2
  • 2 * i > n(节点总数),那么节点 i 没有孩子节点;如果2 * i + 1 > n,那么节点 i 不存在右孩子
  • 如果节点 i 存在孩子节点,那么左孩子节点为 2 * i,右孩子节点为 2 * i + 1

堆的上浮

类似气泡上浮的过程

在最下一层最右边的叶子节点之后加入新元素。如果最下层已满,就新增一层

接着向上调整:如果这个节点的权值大于它的父节点的权值,那么交换。重复此过程,直到不满足或者到根为止

时间复杂度:\(O(log2 n)\)

代码

void push(int x)//加入大根堆 
{
	heap[++ len] = x;//加入最底层 
	int i = len;
	while(i > 1 && heap[i / 2] < heap[i])//不断上浮 
	{
		swap(heap[i / 2], heap[i]);
		i = i / 2;//更新位置 
	}
}

堆的下沉

  1. 将根节点与对的最后一个元素交换
  2. 直接删掉最后一个元素

调整:在该结点的孩子节点中,找到一个最大的于该节点进行交换。重复直到到达底层

时间复杂度:\(O(log\ n)\)

代码

void pop()
{
	//根节点与最后面的元素交换,删除它
	heap[1] = heap[len --];
	int i = 1;
	while(2 * i <= len/*节点 i 存在孩子节点*/)//不断下沉 
	{
		int son = 2 * i;
		if(son < len && heap[son + 1] > heap[son])
		{
			son ++;
		}
		//下沉
		if(heap[son] > heap[i])
		{
			swap(heap[son], heap[i]);
			i = son;
		}
		else break;
	}
}

题目思路(铺垫的多了一点)

最简单的:枚举出每一个和,求最小的 \(n\)

时间复杂度:\(O(n^2)\)

推测得分:50分(自己试)


观察图片:

image

我们可以建一个优先队列,存储 a[i] + b[1] 的值。

每次取出最小的 a[i] + b[j],输出,出队。

把 a[i] + b[j + 1] 入队

重复 \(n\)

这样保证就是最小的 \(n\) 个数

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
struct Node
{
	int A, B, t; //需要知道取的是第几个数
}heap[N];
int len = 0;
void push(int x, int a, int b)
{
	heap[++ len].t = x;
	heap[len].A = a, heap[len].B = b;
	int i = len;
	while(i > 1 && heap[i / 2].t > heap[i].t)
	{
		swap(heap[i / 2], heap[i]);
		i = i / 2;
	}
}

void pop()
{
	heap[1] = heap[len --];
	int i = 1;
	while(2 * i <= len)
	{
		int son = 2 * i;
		if(son < len && heap[son + 1].t < heap[son].t)
		{
			son ++;
		}
		if(heap[son].t < heap[i].t)
		{
			swap(heap[son], heap[i]);
			i = son;
		}
		else break;
	}
}
int a[N], b[N];
int main()
{
	int n;
	cin >> n;
	for(int i = 1; i <= n; i ++)
	{
		cin >> a[i];
	}
	for(int i = 1; i <= n; i ++)
	{
		cin >> b[i];
	}
	for(int i = 1; i <= n; i ++)
	{
		push(a[i] + b[1], i, 1);
	}
	for(int i = 1; i <= n; i ++)
	{
		cout << heap[1].t << ' ';
		if(heap[1].B + 1 <= n/*判断是否越界*/) push(a[heap[1].A] + b[heap[1].B + 1], heap[1].A, heap[1].B + 1);
		pop();
	}
    return 0;
}
posted @ 2023-05-12 18:02  yuzihang  阅读(92)  评论(0)    收藏  举报