牛客小白月赛76 题解

目录

  • 一. 猜拳游戏
    • 1.题目
    • 2.思路
    • 3.代码
  • 二. Kevin喜欢一
    • 1.题目
    • 2.思路
    • 3.代码
  • 三 A加B,A模B
    • 1.题目
    • 2.思路
    • 3.代码
  • 四 MoonLight的运算问题
    • 1.题目
    • 2.思路
    • 3.代码
  • 五 括号序列操作专家
    • 1.题目
    • 2.思路
    • 3.代码
  • 六 Kevin的矩阵
    • 1.题目
    • 2.思路
    • 3.代码

题面下载

一. 猜拳游戏

1.题目

A和B猜拳,A告诉了B自己会出啥,但是A是个老六,他不会按照他告诉你的手势出拳,而是选择自己所认为一定能战胜你的手势。

现在你已经看透了他的小心思。请问,在知道他告诉你他要出什么手势的情况下,你应该出什么手势才能取胜?

2.思路

因为按照题目意思A会出你按照他之前说的手势赢的那个手势,所以只要你出他之前出的那个手势就行。

3.代码

#include
using namespace std;

int main(){
    string s; cin >> s;
    cout << s << endl;
    return 0;
}

二. Kevin喜欢一

1.题目

现在有一个文本框,里面有一个1;
你可以进行:选中文本输入框中的部分或全部字符,将选中的字符复制到剪贴板,然后立即将复制的内容粘贴到文本输入框末尾。
问最少多少次操作达成恰好n个数字1?

2.思路

简单贪心,在当前1的个数小于n的时候采取增长最快的操作,即复制全部加到里面(*2操作),最后一次操作加上 n - now 的 1就行

3.代码

#include
using namespace std;

#define int long long

signed main(){
    int _; cin >> _;
    while(_--){
        int n, t = 1, res = 0; cin >> n;
        while(t < n){
            res ++;
            t = t * 2;
        }
        cout << res << endl;
    }
    return 0;
}

三 A加B,A模B

1.题目

牛客小白月赛76 题解_第1张图片

2.思路

首先因为 a % b = m 可以推出==> a >= m;
因此我们可以先让t = n - m;
这时我们有 a % b < t
所以有解的情况一定是 t > m, 这个时候我们可以构造 a = m, b = t;

3.代码

#include
using namespace std;
int n, m, a, b;
int main(){
    int _; cin >> _;
    while(_--){
        x = -1;
        cin >> n >> m;
        if(n <= m) cout << -1 << endl;
        else{
            int t = n - m;
            if(m == 0){
                a = 0, b = n;
                cout << a << " " << b << endl;
                continue;
            }
            if(m < t){
                a = m, b = t;
                cout << a << " " << b << endl;
            }else{
                cout << -1 << endl;
            }
        }
    }
    return 0;
}

四 MoonLight的运算问题

1.题目

牛客小白月赛76 题解_第2张图片

2.思路

两种操作谁大就用谁
我们有 x + a <= x * a =====> x > 1 && a > 1;
因此我们从左向右遍历一遍进行操作就行,但是得注意我们当前记录的值是存在取余操作的,我们得判断是否进行过取余操作,即不能简单地认为x的值就是当前记录的值

3.代码

#include
using namespace std;
#define int long long
const int N = 2e5 + 10, mod = 998244353;
signed main(){
    int _; cin >> _;
    while(_--){
        int n, x = 0, f = 0; cin >> n;
        for(int i = 1; i <= n; i++){
            int a; cin >> a;
            if(a < 2 || (x < 2 && !f)){
                x += a;
            }else{
                x = x * a;
            }
            if(x >= mod) f = 1;
            x %= mod;
        }
        x %= mod;
        cout << x << endl;
    }
    return 0;
}

五 括号序列操作专家

1.题目

你有一个括号序列,你需要把该序列变成合法的

为了把这个括号序列变合法,每次可以进行下面的操作:

      交换任意两个相邻括号。

如果无论怎么操作都无法使括号序列变合法,输出 −1;
否则请输出他需要做的最少的操作次数。

2.思路

首先我们确定无解的情况,因为我们进行交换的次数可以是无限的,所以只要左右括号的数量相等就一定可以将序列恢复成合法的,无解情况就是数量不相等。

最少操作情况:

对于每个左括号,如果前面有未匹配的右括号,则我们去向前匹配右括号,因为如果向后去匹配最近的右括号,会造成该左括号与匹配的右括号之间的左括号向前移动,不是最优选择。所以左括号向前匹配未匹配的右括号是一种最优选择;
向前匹配我们优先匹配最前的那个右括号,因为我们如果匹配后面的右括号,则后面接下来的左括号依然需要走上这段路程,而且还需要加上前面匹配的左括号的距离,即要跨过中间当前匹配好的括号组距离,而如果每个左括号匹配最前面的右括号就可以使前面的右括号右移,减少前面右括号与后面左括号的距离,也可以避免需要跨过已经匹配好的括号组。

3.代码

#include
using namespace std;

#define int long long
const int N = 200010;
char s[N];
signed main(){
    int _; cin >> _;
    while(_--){
        int n, cnt = 0, res = 0; cin >> n;
        for(int i = 1; i <= n; i++){
            cin >> s[i];
            if(s[i] == '(') cnt++;
            else cnt--;
        }
        if(cnt){
            puts("-1");
        }else{
            for(int i = 1, l = 0, r = 0; i <= n; i++){
                if(s[i] == ')'){
                    if(l > 0){
                        l--;
                    }else{
                        r++;
                    }
                }else{
                    if(r > 0){
                        res += r;
                        r--;
                    }else{
                        l++;
                    }
                }
            }
            cout << res << endl;
        }
    }
    return 0;
}

六 Kevin的矩阵

1.题目

牛客小白月赛76 题解_第3张图片
牛客小白月赛76 题解_第4张图片
牛客小白月赛76 题解_第5张图片

2.思路

首先明确一点:我们找到当前列数下的操作最小值需要的复杂度是o(n),n为数组长度
显然我们首先想到的是暴力枚举列数,求得最小值,这种方法复杂为n方
进行优化----------------->
我们取最坏情况列数m = 1, n = 2e5时候,可以发现一种构造:
令 t = sqrt(n);
把列数扩展成t列,开销为 t - m;
再随便选一列,花费 n / t 的开销调换成目标值
因此我们的最大开销就是 2 * t - m < 2 * t;
我们枚举 max(m - 2 * t, 1) 到 min(m + 2 * t, n)之间的列数就行;

3.代码

#include
using namespace std;

const int N = 200010;

int a[N], n, m, p;

int main(){
    int _; cin >> _;
    while(_--){
        cin >> n >> m >> p;
        for(int i = 1; i <= n; i++){
            cin >> a[i];
        }
        int b = 2 * sqrt(n + 1) + 7, res = 1e9;
        for(int i = max(m - b, 1); i <= min(m + b, n); i++){
            for(int j = 1; j <= i; j++){
                int t = 0;
                for(int k = j; k <= n; k += i){
                    if(a[k] != p) t++;
                }
                res = min(res, abs(i - m) + t);
            }
        }
        cout << res << endl;
    }
    return 0;
}

你可能感兴趣的:(算法比赛题解,笔记,算法)