2023年蓝桥杯省赛真题 蓝桥杯 整数删除 知识点:堆查找最小值+链表删除元素

176 阅读4分钟

1.整数删除 - 蓝桥云课 (lanqiao.cn)

视频解析: [蓝桥杯]真题讲解:整数删除(暴力、链表与优先队列)_哔哩哔哩_bilibili

暴力做法

模拟

首先要找一下最小值i,然后把a[i]删掉。做法就是遍历一遍数组,复杂度是O(n)。

第二步,找到最小值的邻居,给邻居加上最小值。

注意,这里的邻居不是i-1,i+1,因为万一a[i-1],a[i+1]已经被删掉了呢?我们是不是就要2往后往前继续找其他的邻居?

这个同样需要遍历一遍数组,也是O(n)的复杂度,总共就是O(n^2)的复杂度。

image.png只能过部分数据。

image.png

#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)时间内找到最小值的邻居呢?如下图所示, 我们用一个双向链表: image.png 其中

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.

image.png 现在假设我们找到最小值a[i]了,然后r[ i]已经被删掉。那我们想找新的 r[i]是不是就应该如下图所示:

image.png

r[l[i]]=r[i]   l[i]右边元素是a[i]右边的元素

同理,当a[i]左边的元素被删除时也是如此:

l[r[i]]=l[i]  r[i]左边元素的下一个节点是r[i]右边的元素

最后我们还需要用一个st数组来帮我们判断当前最小值的邻居是否被删除,如果被删除了,那么新取出来的邻居肯定和st 之前保存的邻居的值不一样,如下:

image.png

删掉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;
}

image.png