牛客寒假算法基础训练营day-3

2020-05-04  本文已影响0人  _NewMoon

只补了A-E,有机会再补后面的
官方题解地址:https://ac.nowcoder.com/discuss/365889?type=101&order=0&pos=2&page=3&channel=-1

A.欧几里得

gcd(1,0) -----------------------------------------------answer = 0
gcd(2,1) - gcd(1,0) ---------------------------------answer = 1
gcd(3,2) - gcd(2,1) - gcd(1,0) --------------------answer = 2
gcd(4,3) - gcd(3,2) - gcd(2,1) - gcd(1,0) ------answer = 3
....
这是一个f[0] = 1,f[1] = 3,f[2] = 5,f[3] = 8...的斐波那契数列,所以f[n] = f[n-1] + f[n-2]

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;
typedef long long LL;


LL dp[100];

int main()
{
    int T;
    cin>>T;
    
    dp[2] = 5;
    dp[1] = 3;
    dp[0] = 1;
    for(int i = 3; i<=100; i++) dp[i] = dp[i-1] + dp[i-2];
    while(T--)
    {
        int n;
        cin>>n;
        cout<<dp[n]<<endl;
        
    }
    return 0;
}

B.括号序列

数据好像被加强了,用stack好像T了,数组模拟一下栈就ok了

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <string>
#include <stack>

using namespace std;
const int N = 1e6+100;

string str;
char s[N];
int main()
{
    cin>>str;
    int tp = 0;  //栈顶
    bool ans = true;
    int n = str.length();
    
    for(int i = 0; i<n; i++)
    {
        if(str[i] == '(' || str[i] == '[' || str[i] == '{')
             s[tp++] = str[i];
        else if(str[i] == ')')
        {
            if(!tp || s[tp-1] != '('){
                ans = false;
                break;
            }
            tp--;
        }
        else if(str[i] == ']')
        {
            if(!tp || s[tp-1] != '['){
                ans = false;
                break;
            }
            tp--;
        }
        else if(str[i] == '}')
        {
            if(!tp || s[tp-1] != '{'){
                ans = false;
                break;
            }
            tp--;
        }
    }
    
    if(tp)
        ans = false;
    if(ans) puts("Yes");
    else puts("No");
    
    return 0;
}

C.子段乘积

链接:https://ac.nowcoder.com/acm/contest/3005/C

题目描述

给出一个长度为 n 的数列 a1​,a2​,…,an​,求其长度为 k 的连续子段的乘积对 998244353 取模余数的最大值。

输入描述:

第一行两个整数n,k。 n≤200000,0≤ai ≤2^(30)−1
第二行n个整数

输出描述:

输出一个整数,代表最大余数。

尺取法,定义两个变量L和R,让R一直移动,若当前数字不为0,添加到res中去,当尺子(区间)的长度等于k时,更新答案,并让L向右移动一步,那res就需要除去L未移动前的那个位置上的值,因为题目要我们求的是乘积模998..的值,所以除法我们可以用乘法逆元转变成乘法,这里考虑的是没有遇到数字0,如果R遇到数字0,直接抛弃前面的区间,L = R+1,直接从0的下一个位置开始,继续移动尺子...

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

const int N = 2e5+100, mod = 998244353;
typedef long long LL;
LL a[N];
LL ans,n,k;

LL qmi(LL a,LL k){
    LL res = 1;
    while(k){
        if(k&1) res = res*a%mod;
        k>>=1;
        a = a*a%mod;
    }
    return res;
}
int main()
{
    cin>>n>>k;
    for(int i = 1; i<=n; i++) cin>>a[i];
    
    LL l = 1, r = 1,res = 1;
    for(; r<=n; ){
        if(a[r] != 0){
            res = res*a[r]%mod;
            if((r-l+1)>=k) {
                ans = max(ans,res);
                res = res * qmi(a[l],mod-2)%mod;
                l++;  //向右移动一格
            }
        }else{
            l = r+1;
            res = 1;
        }
        r++;
    }
    cout<<ans<<endl;
    return 0;
}

D.子段异或

链接:https://ac.nowcoder.com/acm/contest/3005/D

题目描述

输入一个数列a,你需要输出其中异或值为0的不同子段的数量。一个子段[l,r](1≤l≤r≤n)的异或值为a(l) ^ a(l+1) ^ a(l+2) ^ ... ^ a(r)
两个子段被视为相同的,当且仅当其开始和结束位置均对应相同。

输入描述:

第一行一个整数 n ,代表数列长度。
第二行 n 个整数,代表数列。

输出描述:

输出一个整数,代表答案。

由上式可知,②^③ = ①,所以要使①为0,那么②^③ = 0,即S(L-1) = S(R),S数组是a数组的异或前缀,在遍历过程中用map维护即可

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <map>

using namespace std;

const int N = 2e5+100;
typedef long long LL;

LL a[N],s[N];
LL ans,n;
map<LL,LL> h;

int main()
{
    cin>>n;
    for(int i = 1; i<=n; i++){
        cin>>a[i];
        s[i] = s[i-1] ^ a[i];   //异或前缀
        if(s[i] == 0) ans ++;  //[1,i]是答案,直接++
        ans += h[s[i]];
        h[s[i]] ++;
    }
    cout<<ans<<endl;
    return 0;
}

E.最小表达式

链接:https://ac.nowcoder.com/acm/contest/3005/E

题目描述

给出一个包含数字1-9和加号的字符串,请你将字符串中的字符任意排列,但每种字符数目不变,使得结果是一个合法的表达式,而且表达式的值最小。输出那个最小表达式的值

合法的表达式的定义如下:

保证给出的表达式经过重排,存在一个合法的解。

输入描述:

一行输入一个字符串,仅包含数字1-9和加号+。

字符串的长度小于5∗10^5

输出描述:

一行输出一个数字,代表最小的解。
备注:
注意,答案长度可能长达500000个字符。

统计每个数字出现的次数,因为要我们求的是最小值,所以我们想让大的数放在权值低的地方,先把每个加数的个位填满,再填十位,百位...加数的数量比+号的数量多1,数字用完后,模拟一下十进制加法就行了(这里感叹一下,题解的代码实现真的很强,学习了)

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <string>

using namespace std;

const int N = 5e5+100;
typedef long long LL;

string s;
int a[N];   
int cnt[10];   //存储每个数字的个数
int add;    //记录+号的个数,加数的个数=add+1
int main()
{
    cin>>s;
    int n = s.size();
    for(int i = 0; i<n; i++){
        if(s[i] == '+') add++;
        else cnt[s[i]-'0'] ++;
    }
    add++;  //加数的个数
    int now = 0,t = 0;  
    //now代表当前的数字应该放到数字的哪一位
    //t用来计数,当前操作的是哪一个加数
    for(int i = 9; i>=1; i--){
        while(cnt[i]){
            a[now] += i;
            cnt[i]--;   //用掉了一个i
            t = (t+1) % add;
            if(!t) now ++;   //所有数的个位累加完毕
            //第一个t从0到add,a[now] = 9 + 9 + 8 + 8 + 7
            //(23984692+238752+2+34+为例)=》2369+2359+248+248+237=5461
        }
    }
    
    //模拟一下加法
    for(int i = 0; i<N; i++){
        a[i+1] = a[i+1] + a[i]/10;     //进位
        a[i] %=10;   
    }
    
    //输出结果,从高位向低位输出
    bool out = false;
    for(int i = N; i>=0; i--){
        if(out || a[i]){
            cout<<a[i];
            out =true;
        }
    }
    return 0;
}

F.树上博弈

~

上一篇 下一篇

猜你喜欢

热点阅读