牛客周赛 Round 37 全题解

本文解析了牛客周赛中的六道题目,涉及签到、排序、枚举与数学优化、博弈论、图搜索(BFS)和深度优先搜索(DFS)以及动态规划(DP)和背包问题。通过实例代码展示了如何解决这些基于不同算法策略的问题。
摘要由CSDN通过智能技术生成

牛客周赛37 A - F

题目知识点
A签到
B暴力
C暴力 + 数学
D博弈论
EBFS
FDFS + 剪枝优化 or DP(背包)

A 雾之湖的冰精

【签到题】

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int, int>
#define pdd pair<double, double>
#define pll pair<ll, ll>
#define endl '\n'
typedef long long ll;
inline void solve()
{
    int a, b;
    cin >> a >> b;
    if(a+b <= 9) cout << "Yes" << endl;
    else cout << "No" << endl;
}
int main()
{
    cout << fixed << setprecision(10);
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}

B 博丽神社的巫女

【签到】【暴力】
给a数组排个序,从前往后找到第一个大于x的位置即可。

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int, int>
#define pdd pair<double, double>
#define pll pair<ll, ll>
#define endl '\n'
typedef long long ll;
inline void solve()
{
    int n, x;
    cin >> n >> x;
    vector<int> a(n+1);
    for(int i=1; i<=n; i++)
        cin >> a[i];
    sort(a.begin()+1, a.end());
    for(int i=1; i<=n; i++) {
        if(a[i] > x) {
            cout << i-1 << " " <<  x - a[i-1] << endl;
            return;
        }
    }
    cout << n << " " << x-a[n] << endl;
}
int main()
{
    cout << fixed << setprecision(10);
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}

C 红魔馆的馆主

【暴力】+【数学】
赛后数据加强了,update一下正解
——2024.3.22
同样是枚举,不过稍加推导,可以避免超出long long的范围。
并且处理了添加的数字串包含前导零的情况(例如:05其实会比115更优)
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int, int>
#define pdd pair<double, double>
#define pll pair<ll, ll>
#define endl '\n'
typedef long long ll;

inline void solve()
{
    ll n;
    cin >> n;
    if(n % 495 == 0) {
        cout << -1 << endl;
        return;
    }
    n = n % 495;
    for(int i=0; i<=9; i++) {
        if((n*10 + i) % 495 == 0) {
            cout << i << endl;
            return;
        }
    }
    for(int i=0; i<=99; i++) {
        if((n*100 + i) % 495 == 0) {
            if(i < 10) cout << 0;
            cout << i << endl;
            return;
        }
    }
    for(int i=0; i<=495; i++) {
        if((n*1000 + i) % 495 == 0) {
            if(i < 100) cout << 0;
            if(i < 10) cout << 0;
            cout << i << endl;
            return;
        }
    }
}
int main()
{
    cout << fixed << setprecision(10);
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}

赛时AC的原题解
【暴力】
这题的数据比较善良,暴力就可以过。
其实按理说,这道题严格按照1e18的范围的话,暴力写是会爆long long,但数据应该是没有极端的1e18这样的数据。
暴力的做法就是从1位数开始枚举枚举后面添加数字,其实比较保守的是枚举到后4位,但枚举到3位数就过了。
可能做法有点假,但被我水过了。

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int, int>
#define pdd pair<double, double>
#define pll pair<ll, ll>
#define endl '\n'
typedef long long ll;

inline void solve()
{
    ll n;
    cin >> n;
    if(n % 495 == 0) {
        cout << -1 << endl;
        return;
    }
    for(int i=0; i<=9; i++) {
        if((n*10+i) % 495 == 0) {
            cout << i << endl;
            return;
        }
    }
    for(int i=0; i<=9; i++) {
        for(int j=0; j<=9; j++) {
            if(((n*10+i)*10+j) % 495 == 0) {
                cout << i*10+j << endl;
                return;
            }
        }
    }
    for(int i=100; i<=999; i++) {
        if((n*1000 + i) % 495 == 0) {
            cout << i << endl;
            return;
        }
    }
}
int main()
{
    cout << fixed << setprecision(10);
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}

D 迷途之家的大贤者

【博弈论】
小紫的目标是让字典序更小,那么最后剩下的字符串一定是只有单个字符。
而为了不让小红有操作的余地,小紫的最优策略就是在第一次操作就删得只剩一个字符。
那么,与其等着小紫来删,作为先手的小红不如第一轮就删的只剩一个而留下更大的字符。
由于删除的子串必须是连续的,一次性删除之后留下的只能是第一个元素或最后一个元素。
所以输出 m a x ( s [ n − 1 ] , s [ 0 ] ) max(s[n-1], s[0]) max(s[n1],s[0]) 即可。

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int, int>
#define pdd pair<double, double>
#define pll pair<ll, ll>
#define endl '\n'
typedef long long ll;
inline void solve()
{
    int n;
    string s;
    cin >> n >> s;
    cout << max(s[n-1], s[0]) << endl;
}
int main()
{
    cout << fixed << setprecision(10);
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}

E 魔法之森的蘑菇

【BFS】
常规的地图上跑BFS,不过这题加了点创新,如果没有遇到蘑菇只能按一个方向走。这点其实挺好办的,在BFS队列中加一个维度:通往下一个点的方向。
由于这道题方向也是一个维度,走到同一个点时,下一个方向不同,算作不同情况 。因此,bfs记录已走过的点,也必须记录三个维度。

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int, int>
#define pdd pair<double, double>
#define pll pair<ll, ll>
#define endl '\n'
typedef long long ll;
inline void solve()
{
    int n, m;
    cin >> n >> m;
    vector<vector<char>> mp(n+1, vector<char>(m+1));
    int sx, sy;
    for(int i=1; i<=n; i++)
    for(int j=1; j<=m; j++) {
        cin >> mp[i][j];
        if(mp[i][j] == 'S') {
            sx = i;
            sy = j;
        }
    }
    vector<int> dx = {1, 0, 0, -1};
    vector<int> dy = {0, 1, -1, 0};
    queue<array<int, 4>> q;
    set<array<int, 3>> vis; // 记录已经走过的点
    // 四个方向都入队
    for(int i=0; i<4; i++) {
        q.push({sx, sy, 0, i});
        vis.insert({sx, sy, i});
    }
    while(!q.empty()) {
        // x y坐标 tot步数 d方向 
        auto [x, y, tot, d] = q.front();
        q.pop();
        if(mp[x][y] == 'T') {
            cout << tot << endl;
            return;
        }
        if(mp[x][y] == '.' || mp[x][y] == 'S') {
            int xx = x + dx[d];
            int yy = y + dy[d];
            if(xx>=1 && xx<=n && yy>=1 && yy<=m) {
                if(!vis.count({xx, yy, d})) {
                    q.push({xx, yy, tot+1, d});
                    vis.insert({xx, yy, d});
                } 
            }
        }
        if(mp[x][y] == '*') {
            for(int i=0; i<4; i++) {
                if(i+d != 3) {
                    int xx = x + dx[i];
                    int yy = y + dy[i];
                    if(xx>=1 && xx<=n && yy>=1 && yy<=m) {
                        if(!vis.count({xx, yy, i})) {
                            q.push({xx, yy, tot+1, i});
                            vis.insert({xx, yy, i});
                        } 
                    }
                }
            }
        }
    }
    cout << -1 << endl;
}
int main()
{
    cout << fixed << setprecision(10);
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int t;
    cin >> t;
    while(t --) {
        solve();
    }
    return 0;
}

F 三途川的摆渡人

【DFS】【剪枝优化】
重复的数肯定是最好去掉,那么用set来存数就好。
又由于 a ≤ 200 a≤200 a200 所以实际上的数并不多,跑个加剪枝的DFS暴力枚举留下的数即可。

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int, int>
#define pdd pair<double, double>
#define pll pair<ll, ll>
#define endl '\n'
typedef long long ll;
inline void solve()
{
    int n;
    cin >> n;
    vector<int> a(n+1);
    int s = -1;
    set<int> st;
    for(int i=1; i<=n; i++) {
        cin >> a[i];
        if(i == 1) s = a[i];
        else s &= a[i];
        st.insert(a[i]);
    }
    if(s) {
        cout << -1 << endl;
        return;
    }
    vector<int> b;
    for(int x: st) {
        b.push_back(x);
    }
    int m = b.size();
    int res = m;
    map<int, bool> mp;
    function<void(int, int)> dfs = [&](int s, int tot) {
        if(tot >= res) return;
        if(s == 0) {
            res = min(res, tot);
            return;
        }
        for(int i=0; i<m; i++) {
            if(mp[b[i]]) continue;
            mp[b[i]] = 1;
            dfs(s&b[i], tot+1);
            mp[b[i]] = 0;
        }
    };
    for(int i=0; i<m; i++) {
        mp[b[i]] = 1;
        dfs(b[i], 1);
        mp[b[i]] = 0;
    }
    cout << n - res << endl;
}
int main()
{
    cout << fixed << setprecision(10);
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int t;
    cin >> t;
    while(t --) {
        solve();
    }
    return 0;
}

【DP】【背包】

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int, int>
#define pdd pair<double, double>
#define pll pair<ll, ll>
#define endl '\n'
typedef long long ll;
inline void solve()
{
    int n;
    cin >> n;
    vector<int> a(n+1);
    int s = -1;
    set<int> st;
    for(int i=1; i<=n; i++) {
        cin >> a[i];
        if(i == 1) s = a[i];
        else s &= a[i];
        st.insert(a[i]);
    }
    if(s) {
        cout << -1 << endl;
        return;
    }
    vector<int> b;
    b.push_back(0);
    for(int x: st) {
        b.push_back(x);
    }
    int m = b.size()-1;
    vector<vector<int>> dp(m+5, vector<int>(300, 1e9));
    for(int i=1; i<=m; i++) {
        dp[i][b[i]] = 1;
        for(int j=0; j<256; j++)
            dp[i][j] = min(dp[i][j], dp[i-1][j]);
        for(int j=0; j<256; j++)
            dp[i][j&b[i]] = min(dp[i][j&b[i]], dp[i-1][j] + 1);
    }
    if(dp[m][0] == 1e9) cout << -1 << endl;
    else cout << n - dp[m][0] << endl;
}
int main()
{
    cout << fixed << setprecision(10);
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int t;
    cin >> t;
    while(t --) {
        solve();
    }
    return 0;
}
  • 43
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值