牛客小白月赛65个人题解A-E

1. 牛客小白月赛65

A. 牛牛去购物

题意:给定n元,购买价格为a元的篮球和价格为b的篮球,数量不定,要使得花掉的钱最多,也就是剩余的钱数最少,求这个值 (1 <= n, a, b <= 1000)
思路:因为数据范围很小, 故直接双重循环暴力枚举购买篮球和足球的数量即可,注意一些取值范围,然后取min可得剩余的钱数最小值
#include <bits/stdc++.h>
using namespace std;

#define int long long
#define endl '\n'

void solve()
{
    int n, a, b;
    cin >> n >> a >> b;
    
    int res = 1e8;
    
    for (int i = 0; i <= n / a; i ++)
    {
        for (int j = 0; i * a + j * b <= n; j ++)
        {
            res = min(res, n - i * a - j * b);
        }
    }
    cout << res << endl;
}


signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL), cout.tie(NULL);
    int T;
//     cin >> T;
    T = 1;
    while (T --) solve();
    return 0;
}

B.牛牛写情书

题意:给定s1字符串(仅包含小写字母a-z),而后对其某些位置插入s2 = “0123456789±*|,.~!@#$%^&()[]{}'”;:?<>/" 的子串,然后给定s3字符串,询问s1原串中是否有子串等于s3, (字符串长度 <= 5e3)
思路:由于给出的s1是已经被s2插入过的字符串,可以对照着s2字符串,还原出真正的s1字符串,然后普通的字符串查找子串即可, 值得注意的是:对于’\’ 和 ’ " ’ 这两个字符需要在其前面 加一个 ‘\’ 进行转义
#include <bits/stdc++.h>
using namespace std;

#define int long long
#define endl '\n'


void solve()
{
    int n, m;
    cin >> n >> m;
    
    string s1, s2;
    cin >> s1 >> s2;
    
    string s3 = "0123456789+-*|,.~!@#$%^&()[]{}'\";:?<>\\/";
    map<char, int> mp;
    for (int i = 0; i < s3.size(); i ++) mp[s3[i]] ++;
    
    string s4;
    for (int i = 0; i < s1.size(); i ++)
    {
        if (mp.count(s1[i])) continue;
        s4 += s1[i];
    }
    
    int n2 = s2.size();
//     cout << s4 << endl;
    for (int i = 0; i < s4.size(); i ++)
    {
        string t = s4.substr(i, n2);
        if (t == s2)
        {
            cout << "YES" << endl;
            return;
        }
    }

    cout << "NO" << endl;
}


signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL), cout.tie(NULL);
    int T;
    //cin >> T;
    T = 1;
    while (T --) solve();
    return 0;
}

C.牛牛排队伍

题意:n个人排队, 1号->2号->3号…, 以此类推n-1号排在n号的前面, 有两个操作,操作1:将x号叫走(保证x号此时一定在队伍中); 操作2:求排在x号前面的是谁
思路:对于这种查询前面是某个人的,很容易想到链表,而且是必须是双向链表,因为我们要做的操作是:将x移走,那么就要求x的前一个人是谁,x的后一个人是谁,所以需要pre数组记录x的前一个人是pre[x],同理suff[x]记录x的后一个是suff[x],然后更新的过程画个图直觉体验更好, ps:(这道题卡vector,vector只能过95%的数据) 😡
#include <bits/stdc++.h>
using namespace std;

#define int long long
#define endl '\n'
const int N = 1e6 + 10;
int pre[N], suff[N];

void solve()
{
    int n, k;
    cin >> n >> k;
    
    for (int i = 1; i <= n; i ++) pre[i] = i - 1;
    for (int i = 0; i <= n; i ++) suff[i] = i + 1;
    
    while (k --)
    {
        int id, x;
        cin >> id >> x;
        
        if (id == 1)
        {
            int t_pre = pre[x];
            int t_suff = suff[x];
            
            pre[t_suff] = pre[x];
            suff[t_pre] = suff[x];
        }
        else
        {
            cout << pre[x] << endl;
        }
    }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL), cout.tie(NULL);
    int T;
//     cin >> T;
    T = 1;
    while (T --) solve();
    return 0;
}

D.牛牛取石子

题意:有两堆石子,第一堆有a个,第二堆有b个,牛牛先手,牛妹后手,对于取石子有两种方案:(1) 第一堆取1个,第二堆取2个; (2) 第一堆取2个,第二堆取1个;对于选择的方案,必须保证当前石子 >= 取的石子个数才能取,谁先无法取石子,谁就输了,输入两堆石子的个数,判断谁获胜
思路:这个涉及到对称博弈
  1. 对于后手来说,只要选择与先手对称相反的操作,即先手选择方案1,那么后手就选方案2,这样就可以保证对于两个石堆来说都是 -3,且先手没有办法反制,因此对于两堆石子中的最小值,只要它是 % 3 == 0,那么一定是后手必胜,因为最小值能更快达到0这个必败态
  2. 那么相反,对于如果 两堆石子中的最小值,它不是 % 3 == 0,比如说是 % 3 == 1, 那么先手就取那个有最小值的石堆石子1个,最大值石堆石子2个,这样就可以保证 有最小石子的石堆 % 3 == 0, 这样又变成了情况1了,因此此时是先手必胜
  3. 考虑特殊情况,如果这两个石堆的石子数量一样,那么有最小值的余数有3钟情况:0, 1, 2; 分类讨论可得,对于余数为0的情况,后生必胜(1); 对于余数为1的情况,后手必胜(举个例子:(4,4)), 对于余数为2的情况(5, 5), 先手必胜
#include <bits/stdc++.h>
using namespace std;

#define int long long
#define endl '\n'

void solve()
{
    int a, b;
    cin >> a >> b;
    
    int r = min(a, b) % 3;
    
    if (r)
    {
        if (r == 1 and a == b) cout << "niumei" << endl;
        else cout << "niuniu" << endl;
    }
    else cout << "niumei" << endl;
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL), cout.tie(NULL);
    int T;
    cin >> T;
//     T = 1;
    while (T --) solve();
    return 0;
}

E. 牛牛的构造

题意:给定数组a的长度n,数组a中的数是1~n的排列,求构造一个数组a,使得这个数组恰好存在k个二元组,满足 1 <= i < j <= n && a[i]-a[j] = 2^x (x属于非负整数) 如果不满足一个数组条件输出-1,反之输出这个数组
思路:考虑到要构成这种二元组,那么一定是逆序的,那么即有构造一个前部分顺序,后部分逆序的数组,这样就可以提供这种二元组,同时要考虑每个数字对于二元组个数的贡献
  1. 发现对于每个数字i, 满足 i + 2^x <= n,则有 x = log2(n - i), 再 + 1即为每个数的最大贡献,也就是 x + 1,例如对于数字1,它的贡献是 log2(5-1) + 1 = 3,因为向下取整的原因 + 1,同理对于数字2,log2(5-2) + 1 = 2
  2. 得到每个数字i的贡献后,将k总值减去对应的数字的贡献值,并将其放到最后
  3. 例如 5 5, 可得 3 4 5 2 1, 因为1贡献了3个二元组,2贡献了2个二元组
#include <bits/stdc++.h>
using namespace std;

#define int long long
#define endl '\n'

const int N = 1e6 + 10;
int flag1[N], flag2[N];
int lg[N];

void solve()
{
    int n, k;
    cin >> n >> k;
    
    for (int i = 2; i <= n; i ++) lg[i] = lg[i >> 1] + 1;
    
    for (int i = 1; i <= n; i ++) flag1[i] = 1;
    
    for (int i = 1; i < n; i ++)
    {
        int cha = n - i;
        if (lg[cha] + 1 <= k)
        {
            flag1[i] = 0;
            flag2[i] = 1;
            k -= lg[cha] + 1;
        }
        
        if (k == 0)
            break;
    }
    
    if (k > 0)
    {
        cout << -1 << endl;
        return;
    }
    
    for (int i = 1; i <= n; i ++)
        if (flag1[i]) cout << i << " ";
    
    for (int i = n; i >= 1; i --)
        if (flag2[i]) cout << i << " ";
}


signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL), cout.tie(NULL);
    int T;
//     cin >> T;
    T = 1;
    while (T --) solve();
    return 0;
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
牛客 a卷2022年第四季度的华为题目中,要求考生设计一种高效的数据结构,能够支持以下几种操作: 1. 添加一个元素 2. 删除一个元素 3. 查找是否存在某个元素 4. 返回元素的总数 该数据结构要求满足空间复杂度较小、时间复杂度较低、能够快速地进行查找和修改等多种操作。 想要编写这样一种数据结构,我们可以参考许多已有的经典算法数据结构,如二叉树、哈希表、红黑树等,通过综合利用它们的优点来实现这个问题的解决。 例如,我们可以通过哈希表来存储所有元素的值,并在每个哈希链表的元素中再使用红黑树来进行排序与查找。这样,我们既能够轻松地进行元素的添加和删除操作,也能够在查找较大数据范围和数量时保持较高的速度与效率。同时,由于使用了多个数据结构来协同完成这个问题,我们也能够在空间复杂度上适度地进行优化。 当然,在具体设计这个数据结构的过程中,我们还需要考虑一些实践中的细节问题,例如如何避免哈希冲突、如何处理数据丢失与被删除元素所占用的空间等问题,这都需要相应的算法与流程来进行处理。 总体来看,设计这种支持多种操作的高效数据结构,需要我们具备丰富的算法知识和编程实践能力,同时需要我们在具体处理问题时能够将多种算法数据结构进行有效地结合。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值