问题描述
给定一个长度为N的整数数列:A1,A2,... ,A n。你要重复以下操作K次:每次选择数列中最小的整数(如果最小值不止一个,选择最靠前的),将其删除。并把与它相邻的整数加上被删除的数值。输出K次操作后的序列。
输入格式
将第一行包含两个整数N和K。第二行包含N个整数,A1,A2,A3,--- An.
输出格式
输出N-K个整数,中间用一个空格隔开,代表K次操作后的序列
5 3
1 4 2 8 7
样例输出
17 7
样例变化如下,中括号里的数是当次操作中被选择的数:
[1] 4 2 8 7
5 [2] 8 7
[7] 10 7
17 7
先了解一下队列的大顶堆和小顶堆
//大顶堆(降序,从大到小)
priority_queue <int,vector<int>,less<int> > q;
priority_queue <int> q; //默认大顶堆
//小顶堆(升序,从小到大)
priority_queue <int,vector<int>,greater<int> >q;
搭配pair的使用:
先比较pair 的第一个元素,再比较第二个元素
#include <iostream>
using namespace std;
#include<vector>
#include<queue>
#define int long long
const int N = 5e5 + 10;
int n, k;
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > que;//优先队列维护最小值
int pre[N], ne[N];//维护左边元素和右边元素的下标的下标
int cnt[N], a[N], tmp;//cnt代表下标为i的元素需要修改的值
signed main()
{
cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> tmp;
que.push(make_pair(tmp, i));
pre[i] = i - 1;//下标为i的元素的左边元素的下标为i-1
ne[i] = i + 1;//下标为i的元素的右边的元素的下标为i+1
}
while (que.size() > n - k) {//查找k次
int num = que.top().first;//获取最小值
int id = que.top().second;//获取最小值的下标
que.pop();
/*这里cnt非0,说明在前面的操作过程中,该元素已经进行修改了,但是队列中还没有更新
现在对队列的这个值进行修正,修正后重新查找最小值*/
if (cnt[id]) {
que.push({ num + cnt[id],id });
cnt[id] = 0;
}
else {
int left = pre[id];
int right = ne[id];
cnt[left] += num;//对左边的值进行修改
cnt[right] += num;//对右边的值进行修改
//将该元素在双向链表中删除
ne[left] = right;
pre[right] = left;
}
}
while (!que.empty()) {
int num = que.top().first;
int id = que.top().second;
que.pop();
a[id] = num + cnt[id];
}
for (int i = 1; i <= n; i++) {
if (a[i]) {
cout << a[i] << " ";
}
}
return 0;
}