Loading

2023第十四届蓝桥杯省赛




A.冶炼金属

解题思路

对于每组来说, 最大值肯定是用a/b得到, 而最小值则是用a/(b-1)+1来获取; 最终答案则是要最大值中的最小值和最小值中的最大值;

神秘代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e4 + 10;
int n, m, k;
signed main() {
    cin >> n;
    int minn = 0;
    int maxn = 0x3f3f3f3f;
    for (int i = 1; i <= n; i++) {
        int a, b;
        cin >> a >> b;
        int x = a / b;
        int y = a / (b + 1) + 1;
        maxn = min(maxn, x);
        minn = max(minn, y);
    }
    cout << minn << ' ' << maxn;
    return 0;
}




B.飞机降落

解题思路

本题可以用状态压缩dp来求解, 这题我当时是直接暴力了, 状压dp还是太不熟练了; 本题主要是因为n<10; 所以可以想到用状压dp; f[i]则表示当降落情况为i时所需要的最短时间; 状态计算 f[i] = min(f[i], max(f[i-(1<<j)], t) + l); 注意如果状态计算时的max, 如果没到到达时间就得补上;

神秘代码

#include<bits/stdc++.h>
//#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e4 + 10;
int n, m, t;
int f[N];
struct {
    int a, b, c;
}air [N];
signed main() {
    cin >> t;
    while (t--) {
        cin >> n;
        for (int i = 1; i <= n; i++) {
            int a, b, c;
            cin >> a >> b >> c;
            air[i] = { a,b,c };
        }
        memset(f, 0x3f, sizeof f);
        f[0] = 0;
        for (int i = 1; i < 1 << n; i++) {
            for (int j = 0; j < n; j++) {
                int a = air[j + 1].a, b = air[j + 1].b, c = air[j + 1].c;
                if (i >> j & 1) {
                    int x = f[i - (1 << j)];
                    if (x <= a + b) {
                        f[i] = min(f[i], max(x, a) + c);
                    }
                }
            }
        }
        if (f[(1 << n) - 1] < 0x3f3f3f3f) cout << "YES" << endl;
        else cout << "NO" << endl;
    }
    return 0;
}




C.接龙数列

解题思路

还是一个比较明显的dp问题; 状态表示 f(i,j) 表示前i个数中以j结尾的数列的最大长度; 状态计算分为选和不选两个状态; 对于当前这个数, 我们可以先把不选的最大长度算出来, 因为不选当前这个, 所以我们遍历0~9作为结尾: f[i][j] = f[i - 1][j]; 如果选择当前这个数, 那我们就得选择以这个数开头为结尾的状态, 设a为开头, b为结尾: f[i][b] = max(f[i][b], f[i - 1][a]+1)
对此我们还有一个空间优化版本, 对于上面状态计算中不选当前数的情况, 我们的操作实际上就是把前面以b结尾的状态延续到了第i个数; 比如数列中第2个数是以4结尾的, 从第3个数就断开了, 如果到了第5个数是以4开头的, 那么我们计算f[6][4]就需要用到f[5][4], 而其中f[5][4], f[4][4], f[3][4], 都需要从f[2][4]转移而来; 所以才需要做f[i][j] = f[i - 1][j]的计算; 而空间优化版我们可以把前面的i优化掉, 只保留f[j]; 这样我们的状态计算就变成了f[b] = max(f[b], f[a] + 1); 其中f[b]就是前面最后一个出现的以b结尾的数列的最大长度, 这样就省去了继承的过程

神秘代码

//朴素版本;
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e5 + 10;
int n, m, t;
int f[N][10];
signed main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        string s;
        cin>>s;
        int a = s.front() - '0';
        int b = s.back() - '0';
        for (int j = 0; j <= 9; j++) {
            f[i][j] = f[i - 1][j];
        }
        f[i][b] = max(f[i][b], f[i - 1][a] + 1);
    }
    int maxn = 0;
    for (int i = 0; i <= 9; i++)  maxn = max(maxn, f[n][i]);
    cout << n - maxn;
    return 0;
}

//空间优化版本
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e5 + 10;
int n, m, t;
int f[10];
signed main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        string s;
        cin >> s;
        int a = s.front() - '0';
        int b = s.back() - '0';
        f[b] = max(f[b], f[a] + 1);
    }
    int maxn = 0;
    for (int i = 0; i <= 9; i++)  maxn = max(maxn, f[i]);
    cout << n - maxn;
    return 0;
}




D.岛屿个数

解题思路

本题的难点在于怎么判断外岛和子岛; 如果我们发现一个岛已经形成了环, 那我们就不用去看这个环内部的情况了; 对此我们需要从整个图的最外层入手, 为了防止有的岛就在边缘, 我们可以再给图增加一圈外海; 然后我们可以从外海开始进行bfs, 只要能连接到外海, 就一定不是子岛; 从第二个样例我们发现海水是8向流通, 而陆地是4向流通, 所以我们要用两个bfs来做; 如果外海在蔓延过程中遇到了岛屿, 那么就数量+1并对该岛屿进行bfs, 凡是蔓延过的岛屿或海水都要做个标记, 防止重复;

神秘代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 60;
int n, m, t, idx;
int g[N][N];
bool st[N][N];
int dx[] = { 1,0,-1,0 ,1,1,-1,-1 }, dy[] = { 0,1,0,-1 ,1,-1,1,-1 };
void bfs2(int a,int b) {
    st[a][b] = true;
    for (int i = 0; i < 4; i++) {
        int x = a + dx[i], y = b + dy[i];
        if (x >= 1 && x <= n && y >= 1 && y <= m && !st[x][y] && g[x][y] == 1) {
            bfs2(x, y);
        }
    }
}
void bfs1(int a,int b) {
    st[a][b] = true;
    for (int i = 0; i < 8; i++) {
        int x = a + dx[i], y = b + dy[i];
        if (x >= 0 && x <= n + 1 && y >= 0 && y <= m + 1&&!st[x][y]) {
            if (g[x][y] == 1) {
                bfs2(x, y);
                idx++;
            }
            else bfs1(x, y);
        }
    }
}
signed main() {
    cin >> t;
    while (t--) {
        idx = 0;
        memset(st, false, sizeof st);
        memset(g,0,sizeof g);
        cin >> n >> m;
        for (int i = 1; i <= n; i++) {
            string s;
            cin >> s;
            for (int j = 0; j < s.size(); j++) {
                g[i][j + 1] = s[j] - '0';
            }
        }
        bfs1(0,0);
        cout << idx<<endl;
    }
    return 0;
}




E.子串简写

解题思路

因为字符串的长度是1e5级别, 所以我们只能线性的遍历字符串, 所以很容易想到利用双指针求解, 设子串以a开头, 以b结尾;
注意两点即可: 一是i和j之间距离要大于等于n; 二是假设i第一次到达b时, 这时发现有2个j满足要求, 此时结果为2, 当i到达第二个b时, 有1个j满足要求, 此时结果不仅要+1, 还要把之前的那2个也加上, 故结果是3; 所以我们可以开两个变量idx和res, idx表示当前一共有多少个j满足要求, 而res代表结果, 每次j走完后让res加上idx即可;

神秘代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6 + 10;
int n, m,idx,res;
int f[N];
signed main() {
    cin >> n;
    string s1, s = " ";
    cin >> s1;
    s += s1;
    char a, b;
    cin >> a >> b;
    for (int i = 1, j = 1; i < s.size(); i++) {
        if (s[i] == b) {
            while (j <= i - n + 1) {
                if (s[j] == a) idx++;
                j++;
            }
            res+=idx;
        }
    }
    cout << res;
    return 0;
}




F.整数删除

解题思路

因为要快速得到最小值, 所以可以想到用堆排序; 又因为要快速定位删除并更新其左右的值, 所以可以用双向链表进行维护; 优先队列的元素要用pair类型来储存值和位置; 当进行操作时, 难点在于怎么更新优先队列里的值; 我们发现每次操作都会让值增大, 所以当我们取堆顶的值时, 对照一下看看当前值和链表里面对应位置的值是否相同; 如果不同, 则把原值删除后把链表里的值和位置插入到优先队列里, 这样就实现了优先队列里值的更新;
注意别忘了初始化re[0]和le[n+1];

神秘代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6 + 10;
int n, m,idx;
int le[N], re[N],e[N];
signed main() {
    priority_queue<PII, vector<PII>, greater<PII>> q;
    cin >> n >> m;
    re[0]=1,le[n+1]=n;
    for (int i = 1; i <= n; i++) {
        cin >> e[i];
        q.push({e[i],i});
        le[i] = i - 1;
        re[i] = i + 1;
    }
    while (m--) {
        auto t = q.top();
        q.pop();
        int x = t.first, y = t.second;
        if (x != e[y]) {
            m++;
            q.push({e[y],y});
            continue;
        }
        e[re[y]] += x;
        e[le[y]] += x;
        le[re[y]] = le[y];
        re[le[y]] = re[y];
        
    }
    int i = re[0];
    while (i < n + 1) {
        cout << e[i] << ' ';
        i = re[i];
    }
    return 0;
}
posted @ 2023-06-10 18:32  mostimali  阅读(124)  评论(2编辑  收藏  举报