视频解析: [蓝桥杯]真题讲解:整数删除(暴力、链表与优先队列)_哔哩哔哩_bilibili
暴力做法
模拟
首先要找一下最小值i,然后把a[i]删掉。做法就是遍历一遍数组,复杂度是O(n)。
第二步,找到最小值的邻居,给邻居加上最小值。
注意,这里的邻居不是i-1,i+1,因为万一a[i-1],a[i+1]已经被删掉了呢?我们是不是就要2往后往前继续找其他的邻居?
这个同样需要遍历一遍数组,也是O(n)的复杂度,总共就是O(n^2)的复杂度。
只能过部分数据。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f
const int N = 5e5 + 10;
int a[N];
bool st[N];
int n, k, pos;
signed main()
{
cin.tie(nullptr)->sync_with_stdio(false);
cin >> n >> k;
for (int i = 0; i < n; i++)cin >> a[i];
while (k--)
{
int minn = INF, pos = -1;
//找最小值 和 最小值的坐标
for (int i = 0; i < n; i++)
{
if (minn > a[i] && !st[i])
{
minn = a[i];
pos = i;
}
}
st[pos] = true;
//对相邻的数字进行操作
for (int i = pos - 1; i >= 0; i--) //找左边的邻居1
{
if (!st[i])
{
a[i] += minn;
break; //找到了就不再找了
}
}
//找右边的邻居
for (int i = pos + 1; i < n; i++)
{
if (!st[i])
{
a[i] += minn;
break;
}
}
}
//如果这个位置没有被删掉,那就输出这个位置上的值
for (int i = 0; i < n; i++)
{
if (!st[i])
{
cout << a[i] << " ";
}
}
cout << endl;
return 0;
}
正确解法
第一步:我们去降低求 最小值的复杂度,因为我们只想求最小值, 不想排序,排序的话不仅把最小值找出来了,次小值,次次小值夜找出来了。这里我们可以 用小堆,以 longn的复杂度找出最小值。注意我们这里的堆存放两个值{a[i],i},因为我们即1要保证a[i]是最小的,还要保证a[i]的位置也是最小的。
第二步:
优
化找最小值邻居的复杂度,用链表在O(1)时间内找出。
我们怎么做到在O(1)时间内找到最小值的邻居呢?如下图所示,
我们用一个双向链表:
其中
l[i]表示左边的邻居,
r[i]表示最小值右边的邻居。
这里我们要注意l[i],r[i]的边界问题,假设a[i]在下标为0的位置,那么l[i]就在下标为-1的位置,这个位置是空的,所以我们要给l[i]赋-1,同理,当a[i]在n-1位置的时候,r[i]在n这个位置上,n也是遍历不到的,我们也要给r[i]赋值为-1.
现在假设我们找到最小值a[i]了,然后r[
i]已经被删掉。那我们想找新的
r[i]是不是就应该如下图所示:
r[l[i]]=r[i] l[i]右边元素是a[i]右边的元素
同理,当a[i]左边的元素被删除时也是如此:
l[r[i]]=l[i] r[i]左边元素的下一个节点是r[i]右边的元素
最后我们还需要用一个st数组来帮我们判断当前最小值的邻居是否被删除,如果被删除了,那么新取出来的邻居肯定和st 之前保存的邻居的值不一样,如下:
删掉1之后,r[i]的值由4变成了5,此时4不等于5,那我们就应该把5加入堆中,然后取5出来,而不应该取4.
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
typedef pair<int, int> pii;
const int N = 5e5 + 10;
int a[N], l[N], r[N];
int st[N];
int n, k;
void sovel()
{
cin.tie(nullptr)->sync_with_stdio(false);
cin >> n >> k;
priority_queue<pii, vector<pii>, greater<pii>>q;
for (int i = 0; i < n; i++)
{
cin >> a[i];
q.push({ a[i], i });
st[i] = a[i];
l[i] = i - 1;
r[i] = i + 1;
if (r[i] == n)
r[i] = -1;
}
int cnt = k;
while (k)
{
pii t = q.top();
q.pop();
if (t.first != st[t.second])
{
q.push({ st[t.second], t.second });
continue;
}
k--;
//获取该元素在原数组中的位置
int pos = t.second;
//将该元素的相邻元素加上该数值
if (l[pos] >= 0)
st[l[pos]] += t.first;
if (r[pos] >= 0)
st[r[pos]] += t.first;
//更新相邻点的相邻元素
if (l[pos] >= 0)
r[l[pos]] = r[pos];
if (r[pos] >= 0)
l[r[pos]] = l[pos];
//该元素已经被删除,打标记
st[pos] = -1;
}
for (int i = 0; i < n; i++)
{
if (st[i] != -1)
cout << st[i] << " ";
}
cout << endl;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
// cin >> t;
while (t--)
sovel();
return 0;
}