Day3 蓝桥杯省赛冲刺精炼刷题 —— 排序算法与贪心思维

一、0实现插入排序 - 蓝桥云课

算法代码: 

#include <stdio.h>

const int N = 10000;  // 定义数组的最大大小

int arr[N + 10], temp[N + 10];  // arr为待排序的数组,temp为辅助数组

// 合并操作:将两个已经排好序的子数组合并为一个有序数组
void merge(int arr[], int left, int mid, int right, int temp[]) {
    int i = left;  // 左半部分起始指针
    int j = mid + 1;  // 右半部分起始指针
    int k = left;  // temp数组的起始位置

    // 合并两个子数组
    while (i <= mid && j <= right) {
        if (arr[i] <= arr[j]) {
            temp[k++] = arr[i++];  // 如果左半部分的元素较小,放入temp数组
        } else {
            temp[k++] = arr[j++];  // 否则,放入右半部分的元素
        }
    }

    // 将左半部分剩余的元素放入temp数组
    while (i <= mid) {
        temp[k++] = arr[i++];
    }

    // 将右半部分剩余的元素放入temp数组
    while (j <= right) {
        temp[k++] = arr[j++];
    }

    // 将temp数组中的元素复制回原数组arr中
    for (i = left; i <= right; i++) {
        arr[i] = temp[i];
    }
}

// 归并排序主函数
void merge_sort(int arr[], int left, int right, int temp[]) {
    if (left < right) {
        int mid = (left + right) / 2;  // 计算中间位置
        merge_sort(arr, left, mid, temp);  // 递归排序左半部分
        merge_sort(arr, mid + 1, right, temp);  // 递归排序右半部分
        merge(arr, left, mid, right, temp);  // 合并已排序的左右两部分
    }
}

int main() {
    int n;

    // 输入数组的长度
    scanf("%d", &n);

    // 输入数组的元素
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &arr[i]);
    }

    // 调用归并排序
    merge_sort(arr, 1, n, temp);

    // 输出排序后的数组
    for (int i = 1; i <= n; ++i) {
        printf("%d ", arr[i]);
    }

    return 0;
}

二、0封闭图形个数 - 蓝桥云课 

算法代码:

#include <iostream>
#include <algorithm>
using namespace std;

const int N = 1e6 + 10;

const int D[] = {1, 0, 0, 0, 1, 0, 1, 0, 2, 1};

struct st
{
    int a;
    int b;
} s[N];

int f(int x)
{
    int cnt = 0;
    while (x)
    {
        cnt += D[x % 10];
        x /= 10;
    }
    return cnt;
}

bool cmp(st x, st y)
{
    if (x.b != y.b)
        return x.b < y.b;
    return x.a < y.a;
}

int main()
{
    int n;
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        cin >> s[i].a;
        s[i].b = f(s[i].a);
    }
    sort(s, s + n, cmp);
    for (int i = 0; i < n; i++)
        cout << s[i].a << " ";
    return 0;
}

 三、1.错误票据 - 蓝桥云课

方法一:算法代码:(数组下标逻辑判断)

#include <algorithm>
#include <iostream>
using namespace std;

const int N = 100005;
int a[N];

int main() {
    int n;
    cin >> n;
    int cnt = 0, x;
    while (cin >> x) { // 由于与行数无关,所以直接输入变长数字
        a[++cnt] = x;
    }
    sort(a + 1, a + cnt + 1);
    int x1, x2;
    for (int i = 2; i <= cnt; ++i) {
        if (a[i] == a[i - 1] + 2) { // 缺号
            x1 = a[i - 1] + 1;
        } 
        else if (a[i] == a[i - 1]) { // 重号
            x2 = a[i - 1];
        }
    }
    cout << x1 << ' ' << x2 << endl;
    
    return 0;
}

 方法二:算法代码(哈希表判断)

#include <iostream>
#include <vector>
#include <unordered_map>
#include <climits>  // 用于 INT_MAX 和 INT_MIN
using namespace std;

int main() {
    int N;
    cin >> N;
    cin.ignore();  // 忽略第一行的换行符

    vector<int> ids;
    unordered_map<int, int> count_map;
    int min_id = INT_MAX, max_id = INT_MIN;

    // 读取所有 ID
    for (int i = 0; i < N; ++i) {
        string line;
        getline(cin, line);
        int num = 0;
        for (char c : line) {
            if (c == ' ') {
                ids.push_back(num);
                count_map[num]++;
                min_id = min(min_id, num);
                max_id = max(max_id, num);
                num = 0;
            } else {
                num = num * 10 + (c - '0');
            }
        }
        // 处理行末尾的数字
        if (num != 0) {
            ids.push_back(num);
            count_map[num]++;
            min_id = min(min_id, num);
            max_id = max(max_id, num);
        }
    }

    int duplicate = -1;  // 重号
    int missing = -1;    // 断号

    // 查找重号(出现次数为2的ID)
    for (auto& pair : count_map) {
        if (pair.second == 2) {
            duplicate = pair.first;
            break;
        }
    }

    // 查找断号(在[min_id, max_id]中未出现的ID)
    for (int i = min_id; i <= max_id; ++i) {
        if (count_map.find(i) == count_map.end()) {
            missing = i;
            break;
        }
    }

    cout << missing << " " << duplicate << endl;
    return 0;
}

四、2.三国游戏 - 蓝桥云课

算法代码: 

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

using ll = long long;

const int N = 1e5 + 100;

int A[N], B[N], C[N]; // 存放魏蜀吴三个国家发生对应事件的得分
int battle[N];
int n;

int Battle(int a[], int b[], int c[]) // a获胜,发生最多的事件数
{
    ll sum = 0;                        // 累加当前总贡献
    memset(battle, 0, sizeof(battle)); // battle数组清零
    for (int i = 1; i <= n; i++)       // 此时假设A会赢
    {
        battle[i] = a[i] - b[i] - c[i];
    }
    sort(battle + 1, battle + n + 1); // 将纯贡献进行排序
    int ans = 0;                      // 记录可以发生多少事件使a>b+c
    for (int i = n; i > 0; --i)      // 从大到小枚举
    {
        sum += battle[i]; // 累加贡献
        if (sum > 0) ans ++;
    }
    if (ans == 0) return -1;
    return ans;
}

int main()
{
    scanf("%d", &n);
    // 输入原始数据,A、B、C分别存储魏蜀吴三个国家发生对应事件增加的得分
    for (int i = 1; i <= n; i++)
        scanf("%d", &A[i]);
    for (int i = 1; i <= n; i++)
        scanf("%d", &B[i]);
    for (int i = 1; i <= n; i++)
        scanf("%d", &C[i]);

    int ans_A = Battle(A, B, C);            // 假设魏国获胜,发生最多的事件数
    int ans_B = Battle(B, A, C);            // 假设蜀国获胜,发生最多的事件数
    int ans_C = Battle(C, A, B);            // 假设吴国获胜,发生最多的事件数
    int ans = max({ans_A, ans_B, ans_C}); // 三者取最大值即可
    printf("%d\n", ans);
    return 0;
}

五、1.训练士兵 - 蓝桥云课

算法代码: 

#include <iostream>
#include <algorithm>

using namespace std;
const int N = 1e5 + 100;

// 士兵结构体:p为单独训练次数,c为批量训练次数
struct Soldier {
    int p, c;
} s[N];

using ll = long long;

// 比较函数:按c降序排列,用于排序
bool cmp(Soldier& a, Soldier& b) {
    return a.c > b.c;
}

int main() {
    ll S;
    int n;
    cin >> n >> S;
    for (int i = 1; i <= n; ++i) {
        cin >> s[i].p >> s[i].c;
    }
    
    // 按c值从大到小排序(从s[1]到s[n])
    sort(s + 1, s + n + 1, cmp);

    ll tmp = 0;
    int pos = 0;
    // 找到最后一个累加p总和小于S的士兵位置
    for (int i = 1; i <= n; ++i) {
        tmp += s[i].p;
        if (tmp < S) pos = i;
    }

    int Bt = 0;
    if (pos < n) Bt = s[pos + 1].c; // 批量训练次数取下一个士兵的c值

    // 计算总成本
    ll ans = Bt * S;
    for (int i = 1; i <= pos; ++i) {
        ans += (ll)s[i].p * (s[i].c - Bt); // 单独训练部分的额外成本
    }

    cout << ans << endl;
    return 0;
}

六、1.排个序 - 蓝桥云课

算法代码:

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;
const int N = 1003;
int a[N], p[N], tmp[N];
int n, m;

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }
    for (int i = 1; i <= n; ++i) { // p的顺序实际上没有作用
        cin >> p[i];
        tmp[i] = a[p[i]];
        a[p[i]] = -1; // 标记已经被选中 
    }
    sort(tmp + 1, tmp + m + 1); // p的顺序实际上没有作用,直接进行排序
    int pos = m;
    for (int i = 1; i <= n; ++i) {
        if (a[i] == -1) { // 将被选中值的填充回去
            a[i] = tmp[pos]; // 从大到小填充
            pos--;
        }
    }

    for (int i = 2; i <= n; ++i) {
        if (a[i] > a[i - 1]) { // 如果不是非递减序列
            cout << "NO" << endl;
            return 0;
        }
    }
    cout << "YES" << endl;
    return 0;
}