【蓝桥杯 2023 C++ 省 B】H - 整数删除 补题

【蓝桥杯 2023 C++ 省 B】H - 整数删除 补题

整数删除

题意

给一个数列,然后操作K次,每次删除最小的那个,并且把它相邻的数字都加上它的值

思路

优先队列双链表

  • 优先队列

    可以在每次操作时,快速找到中最小的那个数字,取出来操作

    tuple<long long, int>,前面是val,后面是这个数字在原数列中对应的id,方便操作它相邻的数字

  • 双链表

    搞两个数组,frontback,一个连前一个数字,一个连后一个数字

更新优先队列中的数值

​ 在每次操作的时候,都有可能会对其他数字造成影响,原本在已经在优先队列中的那个最小值,可能被操作完之后,就不是最小的那个数字了。

​ 所以在每次取出数字的时候,都要看当前拿出来的状态,是不是已经被修改完了的

​ 于是建立另一个数组来记录当前拿出来的这个数字,拿出来的时候,是不是可以操作了

modify

  • -1说明这个数字已经被删除
  • 0说明这个数字已经是可以被操作(删除的状态)
  • 大于0说明这个数字还需要被修改,还不是最小值

代码

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <tuple>
using namespace std;
// tuple前面是val,后面是id
typedef tuple<long long, long long> TII;
const int N = 5e5 + 5;
// 双向链表加优先队列
priority_queue<TII, vector<TII>, greater<TII>>q;
// 双链表
int front[N], back[N];
// 标记数组(已经被删除,还是还不能操作,需要修改多少)
long long modify[N];
// 数列数组
long long a[N];
int n, k;
// 读入
void init() {
	cin >> n >> k;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
        // 每一个数都在开始的时候推入队列
		q.push({ a[i],i });
        // 建立双链表(链表里面装的是id)
		front[i] = i - 1;			// 前驱
		back[i] = i + 1;			// 后继
	}
}

void solve() {
	int f, b, id;
	int cnt = 0;
	long long val;
	while (cnt < k) {					// 操作k次,有效操作才让cnt++
		tie(val, id) = q.top();			// tie获取数据,C++11好像不能auto[val,id]
		q.pop();
		if (modify[id] == -1)continue;	// 已经被删除了,不操作
        // 需要被修改之后重新推入队列,这一次拿出来的是无效的,扔掉
		if (modify[id]) {				
			a[id] += modify[id];		// 在原数组上面修改,最后输出数组就行
			modify[id] = 0;				// 修改值归零
			q.push({ a[id],id });		// 重新加入队列当中
			continue;					// 不删除,continue
		}
		modify[id] = -1;				// 有效操作,删除数字就把modify改成-1
		f = front[id]; b = back[id];	// 取出前驱、后继节点的id
		modify[f] += val;				// 做出修改
		modify[b] += val;
		back[f] = b;					// 删除中间那个节点的时候,记得连接前驱后继
		front[b] = f;
		cnt++;							// 有效删除,cnt++
	}
    // 队列非空,有些数字需要被修改,但是还没修改,继续操作一下
	while (!q.empty()) {				
		tie(val, id) = q.top();
		q.pop();
		if (modify[id]) {				// 修改
			a[id] += modify[id];
			modify[id] = 0;
		}
	}
	for (int i = 1; i <= n; i++) {		// 遍历数组,看哪个数字没被删除,就输出
		if (modify[i] != -1) {
			cout << a[i] << " ";
		}
	}
}

int main() {
	init();
	solve();
	return 0;
}

反思

VP的时候看到这题还是很有感觉的,但是细想又觉得有点麻烦,实际已经想到优先队列加上链表了,觉得每次操作完之后,最小的数字可能会发生改变,又想不到用什么来维护(modify),于是乎就没写了,还是得动手写写

posted @ 2025-04-08 18:25  zombieee  阅读(46)  评论(0)    收藏  举报