萌新补题牛客周赛合集

round34

B.

题目内容:小红拿到了一个数组,她修改尽可能少的元素使其变成 非排列 。你能帮帮她吗?
定义排列为一个长度为n的数组,其中1到n每个元素恰好出现一次。

自己做的时候费了好大劲儿,主要是没有写成时间复杂度较低的判排列代码

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int n = 0, i = 0, temp = 0;
    cin >> n;
    vector<int> a(n + 1, 0);
    for( ; i < n; i++) {
        cin >> temp;
        if(temp <= n) {
            a[temp]++; 
        }else{
            cout << 0;
            return 0;
        }
    }
    for(i = 1; i <= n; i++) {
        if(a[i] != 1) {
            cout << 0;
            return 0;
        }
    }
    cout << 1 << '\n' << "1 1000000000";
    return 0;
        
}

判断一个数在一串数字里有没有出现且是否只出现了一次,可以用一个记录数组a,每输入一次就将对应值加1,最后循环遍历判断即可

unorder_set法(新学的):

std::unordered_set 用于存储唯一的元素集合。它提供了快速的插入、查找和删除操作,平均时间复杂度为 O(1)。

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int n = 0;
    cin >> n;
    unordered_set<int> a;
    int temp = 0;
    for(int i = 0; i < n; i++) {
        cin >> temp;
        a.insert(temp);
    }
    for(int i = 1; i <= n; i++) {
        if(a.find(i) == a.end()) {
            cout << 0;
            return 0;
        }
    }
    cout << 1 << '\n' << "1 1000000000";
    return 0;
}

C.

题目内容:小红拿到了一个偶数,她希望你将其切割成尽可能多的偶数。你能帮帮她吗?

输出若干行,从小到大输出每个偶数。

考点是贪心,思路是遇到偶数就切割,遇到奇数就继续往下走直到遇到偶数了再切割,由于输入的偶数范围很大,超出了各种整数能表示的范围,因此使用string。但是使用string进行升序排列时会按照第一个字符的字典序,即“12<3”,因此需要在sort函数里重载一个自定义排序函数(今天新学的)。

自己做的时候吧,切割的思路倒是对的,但是代码实现还是没过关(语言基础真的好差呜呜呜)

#include <bits/stdc++.h>
using namespace std;

bool cmp(string a, string b)
{
    if(a.length() == b.length()) return a < b;
    else return a.length() < b.length();
}
int main()
{
    string s;
    cin >> s;
    vector<string> a;
    string temp;//将s切割成一个个temp字符串,压入a里进行大小比较
    
    for(auto i : s) {
        temp += i;
        if((i - '0') % 2 == 0) {
            a.push_back(temp);
            temp = "";
        }
    }
    sort(a.begin(), a.end(), cmp);
    for(auto v : a) {
        cout << v << '\n';
    }
    return 0;
}

temp里压入偶数就直接切割放入a容器里,遇到奇数就继续往下遍历(妙啊,我当时写了坨什么勾事),然后sort函数里重载了自定义的cmp函数,接受两个字符串,如果两个字符串长度相等,那么返回他们的字典排序,如果长度不相等,那么短的排在长的后面。如果返回值为 true,表示第一个字符串应该排在第二个字符串之前;如果返回值为 false,则表示第一个字符串应该排在第二个字符串之后。

round35

B.小红的数组分配

题目内容:小红拿到了一个长度为2∗n的数组,她希望你将其中所有元素分配到两个长度相等的数组a和b,满足对于1≤i≤n有ai=bi​。你能帮帮她吗?

输入

2
1 4 4 1

输出

4 1
4 1

我的代码(数组法)

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n = 0, temp = 0;
    cin >> n;
    vector<int> vec;
    for(int i = 0; i < 2 * n; i++) {
        cin >> temp;
        vec.push_back(temp);
    }
    
    sort(vec.begin(), vec.end());
    for(int i = 0; i < 2 * n - 1; i += 2) {
        if(vec[i] != vec[i+1]) {
            cout << "-1";
            return 0;
        }
    }
    
    for(int j = 0; j < 2; j++) {
        for(int i = 0; i < 2 * n - 1; i += 2) {
            cout << vec[i] << ' ';
        }
        cout << '\n';
    }
    
    return 0;
}

反思:一个两两能配对的数组,例如1 4 1 4,经过sort后会变成1 1 4 4;通过遍历先检查是不是能两两配对的数组,再输出;注意遍历步长为2,自己做的时候就是没有想到这一点所以写的不仅长还不对。

我的代码(map法)

#include <bits/stdc++.h>
using namespace std;

int main() 
{
    int n = 0, temp = 0;
    cin >> n;
    map<int, int> record;
    for(int i = 0; i < 2 * n; i++) {
        cin >> temp;
        record[temp]++;
    }
    for(auto it : record) {
        if(it.second % 2 != 0) {
            cout << -1;
            return 0;
        }
    }
    vector<int> res;
    for(auto it : record) {
        int cnt = it.second / 2;
        while(cnt--) {
            res.push_back(it.first);
        }
    }
    for(int i = 0; i < 2; i++) {
        for(auto i : res) {
            cout << i << ' ';
        }
        cout << '\n';
    }
    return 0;
}

反思:map左值为该元素的值,右值为为该元素出现的频率,遍历map,频率不为2的倍数直接return;再使用数组res存储去掉搭档后的元素;map平时写的比较少,要注意一下语法。

C.小红关鸡

题目内容:有n个鸡窝排成一排,第i个鸡窝在数轴上的坐标是xi,有一只小鸡会随机的在一个鸡窝中出现。小红准备在数轴上放置两个栅栏,如果小鸡出现在两个栅栏中间(包括端点),则将被小红关住。为了方便管理,两个栅栏之间的最大距离不能超过k。现在小红希望最大化成功“关鸡”的概率,请你帮小红求出这个概率。

我的代码(双指针法)

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int n = 0, k = 0, temp = 0; 
    cin >> n >> k;
    vector<int> pos;
    for(int i = 0; i < n; i++) {
        cin >> temp;
        pos.push_back(temp);
    }
    sort(pos.begin(), pos.end());
    int l = 0, r = 0, ma = 0;
    while(r < n) {
        while(pos[r] - pos[l] > k) {
            l++;
        }
        ma = max(ma, r - l + 1);
        r++;
    }
    cout <<  1.0 * ma / n;
    return 0;
}

反思:题目求的也就是给你给你一段定长k,在k的范围内尽可能圈住多的元素。无论是双指针法还是二分法都要先对数组元素进行sort;然后移动右指针,当双指针覆盖长度大于k时,移动左指针;取k范围内能覆盖的最大长度。

round36

B.小红的小红矩阵构造

题目内容:小红希望你构造一个n行m列的矩阵,满足所有元素之和恰好等于x,且每行、每列的异或和全部相等。你能帮帮她吗?

异或和:将异或的两个数字转换为二进制,比较每一位,相同为0,不同为1

【例】8 xor 12 = 4

                1000 

                1100

异或:     0100

异或和的一个性质:a^b^a = b

代码

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n = 0, m = 0, x = 0;
    cin >> n >> m >> x;
    vector<vector<int>> nums;
    int temp_num = 0;
    for(int i = 0; i < n; i++) {
        vector<int> temp;
        for(int j = 0; j < m; j++) {
            cin >> temp_num;
            temp.push_back(temp_num);
        }
        nums.push_back(temp);
    }
    int sum = 0; 
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < m; j++) {
            sum += nums[i][j];
        }
    }
    if(sum != x) {
        cout << "wrong answer";
        return 0;
    }
    
    set<int> record;
    for(int i = 0; i < n; i++) {
        int temp1 = 0;
        for(int j = 0; j < m; j++) {
            temp1 ^= nums[i][j];
        }
        record.insert(temp1);
    }
    for(int j = 0; j < m; j++) {
        int temp1 = 0;
        for(int i = 0; i < n; i++) {
            temp1 ^= nums[i][j];
        }
        record.insert(temp1);
    }
    if(record.size() > 1) {
        cout << "wrong answer";
        return 0;
    }
    cout << "accepted";
    return 0;
}

反思:自己写的时候主要是死在了“每行、每列的异或和全部相等”,既不知道怎么判断该异或和只出现了一次,又不知道怎么判断“每行”、“每列”;现在新学了判断某个值是否只出现了一次就用set,因为set会自动去重;遍历每行就把行遍历放前面,遍历每列就把列遍历放前面;异或运算符为‘^’

round37

C.红魔馆的馆主

题目内容:小红来到了红魔馆。众所周知,红魔馆的馆主是一只495岁的吸血鬼,所以她非常喜欢495这个数。现在,小红拿到了一个正整数,她想在这个正整数的结尾增加尽可能少的数字,使得该数字变成495的倍数。请你给出任意一个添加方案。

代码(这个代码只过了96%,看来是后来数据加强了):

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
    ll m = 0, i = 0;
    cin >> m;
    __int128 n = m;
    if(n % 495 == 0) return cout << -1, 0;
    for(i = 0; i <= 9; i++) {
        if( (n * 10 + i) % 495 == 0) {
             
            return cout << i, 0;
        }
    }
    
    for(i = 10; i <= 99; i++) {
        if( (n * 100 + i) % 495 == 0) {
             
            return cout << i, 0;
        }
    }
    
    for(i = 100; i <= 999; i++) {
        if( (n * 1000 + i) % 495 == 0) {
             
            return cout << i, 0;
        }
    }
    return 0;
}

反思:使用long long有爆表的可能,所以开__int128,能把范围扩大到1e36;然后就是暴力枚举,n如果带上后缀i就能成为495的倍数的话就直接输出,只开三个循环是因为在整数范围内,任意划定一个495的长度就一定能划到495的倍数。

round38

B.小红的抛弃后缀

题目内容:小红拿到了一个正整数,她准备切掉一个后缀并抛弃,使得剩余部分是9的倍数。小红想知道有多少种不同的操作方案?(1<= x <= 10e100000)

代码:

#include <bits/stdc++.h>
using namespace std;
int main()
{
    string s;
    cin >> s;
    int ans = 0;
    int sum = 0;
    for(int i = 0; i < s.size(); ++i) {
        sum += (s[i] - '0');
        if(sum % 9 == 0) ans++;
    }
    cout << ans;
    return 0;
}

反思:这是一个需要记住的结论,就是判断一个数是不是3/9的倍数,就看它的每一位上数字的和是不是3/9的倍数。例如18拆成1+8,9就是9的倍数。然后遍历字符串的每一位,如果当前和是9的倍数就ans++.

C.小红的字符串构造

题目内容:小红希望你构造一个长度为n的、仅包含小写字母的字符串,其中恰好有k个长度大于1的回文子串。你能帮帮她吗?

输入描述:

两个整数n,k,用空格隔开。
1≤n≤10^5
0≤k≤n/2

代码:

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n = 0, k = 0;
    cin >> n >> k;
    string a = "abc";
    string b = "def";
    string s;
    for(int i = 0; i < k; ++i) {
        s += a[i%3];
        s += a[i%3];
    }
    for(int i = k*2; i < n; ++i) {
        s += b[i%3];
    }
    cout << s;
    return 0;
}

反思:题目条件里k <= n/2,也就是回文串的长度不会超过字符串的长度,那么如果要1个回文串,就可以用“aa”,2个就是“aabb”, 3个就是“aabbcc”,4个就是“aabbccaa”,保证回文串之间不会再次形成回文,余下的部分用“def”一位位填充。

D.小红的平滑值插值

题目内容:小红定义一个数组的“平滑值”为:相邻两数差的绝对值的最大值。现在小红拿到了一个数组。她每次操作可以在两个元素之间添加一个整数(不能添加在第一项前面或者最后一项后面)。小红希望最终数组的平滑值恰好等于k,你能帮小红求出最小的操作次数吗?

代码:

#include <bits/stdc++.h>
using namespace std;
const int MAX = 1e5+9;
int a[MAX];
int n, ma = -1;
long long cnt = 0, k;

int main()
{
    cin >> n >> k;
    for(int i = 1; i <= n; ++i) {
        cin >> a[i];
        if(i >=2) {
            ma = max(ma, abs(a[i]-a[i-1]));
            
        }
    }

    if(ma < k) {
        cout << 1;
        return 0;
    }

    if(ma == k ) {
        cout << 0;
        return 0;
    }

    for(int i = 2; i <= n; ++i) {
        if(abs(a[i]-a[i-1]) > k) {
            int dis = abs(a[i]-a[i-1])/k;
            if(abs(a[i]-a[i-1])%k==0) dis--;
            cnt += dis;
        }
    }
    cout << cnt;
    return 0;
}

反思:假如数组的平滑值刚好等于k,不用操作直接输出0;假如平滑值<k,任选两数之间插入一个更大的数即可,输出1;假如平滑值比k大,此时我们既希望插入的数字少一点,又得保证其中一个平滑值为k,另外<=k,所以应该每k个大小就插入一个数;

5   0

k = 2

5/2 = 2 ...... 1

此时会插入两个数,形成3段距离;由于插入个数永远为距离-1,所以对除法向上取整再-1就是插入的数字个数

5   1

k = 2

在这个例子里,直接插入5/2个数会得到2,但是插入2个不是最小插入方案,插入1个才是最小插入方案(插入3);所以使用向上取整(整除不会再向上)再-1是最严谨的;最后不要忘了开long long,要形成一种习惯。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值