目录

蓝桥杯2023年第十四届省赛真题-整数删除-暴力-链表小根堆

蓝桥杯2023年第十四届省赛真题-整数删除 暴力–>链表+小根堆

来自DOTCPP:

思路:

①每次找到数列中的最小值下标,然后用状态数组st标记它,相当与删除它,之后就不会访问它。

②对最小值下标左边和右边判断一下,看有没有数字,如果有就把最小值加到两边第一个数字。

暴力代码如下(会超时):

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e5+10;

int n, k;
int arr[N];
bool st[N];//记录一个数字有没有选过

signed main(){
    cin >> n >> k;
    for(int i = 1; i <= n; i++) cin >> arr[i];
    
    for(int i = 1; i <= k ; i++){
        //找到最小值在数组中的位置
        int minv = 1e18;
        int ssmin = -1;
        for(int j = 1; j <= n; j++){
            if(minv > arr[j] && !st[j]){
                //更新最小值的坐标
                ssmin = j;
                minv = arr[j];
            }
        }
        //将最小值标记
        st[ssmin] = true;
        //将最小值加到右边第一个数字
        if(ssmin > 1 ){
            for(int m = ssmin; m >= 1; m--){
                if(!st[m]){
                    arr[m] += minv;
                    break;
                }
            }
        }
        //将最小值加到左边第一个数字
        if(ssmin < n){
            for(int k = ssmin; k<= n; k++){
                if(!st[k]){
                    arr[k] += minv;
                    break;
                }
            }
        }
    }
    for(int i =1; i <= n; i++){
        if(st[i]) continue;
        cout << arr[i] << " ";
    }
    return 0;
}

https://i-blog.csdnimg.cn/direct/2cb93403c2234c6d8482271095662059.png

代码优化:

上面代码K次排序,在N个数中找到最小值。时间复杂度爆炸,我们需要对它优化。需要用到小根堆来记录最小值的下标,同时对于最小值的下标两边,我们用链表记录,会减少很多时间。

小根堆+链表:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e5+20;

#define x first
#define y second
typedef pair<int, int> PII;

int n, k;
int arr[N];//数据
//优先队列-小根堆
//q第一个参数为值,第二个参数为这个值的下标
priority_queue<PII, vector<PII>, greater<PII>> q;
//链表
int l[N], r[N];

signed main(){
    cin >> n >> k;
    for(int i = 1; i <= n; i++){
        cin >> arr[i];
        //存入优先队列
        q.push({arr[i],i});
        //记录左右两边坐标 最左边和最右边都记为-1
        l[i] = i-1;
        r[i] = i+1;
        //最右边记为-1
        if(r[i] == n+1){
            r[i] = -1;
        }
    }
    //开始K次操作
    while(k--){
        //取出最小值
        auto t = q.top();
        //删除最小值
        q.pop();
        //最小值的 值、坐标
        int num = t.x, pos = t.y;
        
        //后面pos两边第一个数加上num后,q中的值发生改变
        //我们没有记录 因此我们要判断一下
        if(num != arr[pos]){
            //我们之前删除了这个数,然后在更新一下
            //因为我们取得值不是更新过的值(这个值是原来的,没有加上最小值)
            q.push({arr[pos], pos});
            k++;//**同时这次操作要重新来过 k++
            continue;
        }
        //对删除的数标记一下,方便输出
        arr[pos] = -1;
        //对左右两边第一个数加上最小值
        //对删除最小值下标的链表更新,即pos左边和右边链接在一起
        if(l[pos] >= 1){
            //左边数加上最小值
            arr[l[pos]] += num;
            //pos左边链接到pos右边
            r[l[pos]] = r[pos];
        }
        if(r[pos] >= 1){
            //右边数加上最小值
            arr[r[pos]] += num;
            //pos右边链接到pos右边
            l[r[pos]] = l[pos];
        }
    } 
    
    for(int i = 1; i <= n; i++){
        if(arr[i] != -1){
            cout << arr[i] << " ";
        }
    }
    return 0;
}

注意:代码中pos左右两边的数的下标,在arr数组中加上最小值之后,在q中是没有更新的,因此我们要判断一下, q中取出最小值的下标和我们在arr数组中相同下标的元素的值是否相等 ,不相等的话,就要更新一下。同时,这次操作没有对pos两边的元素进行操作,则k++。