牛客小白月赛94(A~D题讲解附代码 EF代码已补)【语法入门 前缀和 简单构造 枚举答案】

牛客小白月赛94【A~D题讲解附代码】【EF代码已补】

比赛链接:牛客小白月赛94

前言:蒟蒻很菜,好久没做题了,状态不是很好【哭】,平常也要多做题呀!

A. 小苯的九宫格

题意

给你一个乱序的九宫格,但你仍按照顺序的九宫格情况打字,打了一串字符,问实际打出来的是什么字符。

思路

无,简单循环。

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

int main(void)
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int g[11];
    for(int i = 1;i <= 9;i ++) cin >> g[i];
    string s;cin >> s;
    for(int i = 0;i < s.length();i ++)
    {
        cout << g[s[i] - '0'];
    }
    return 0;
}

B. 小苯的好数组

题意

给你一数组,找出一个最长子序列,这个子序列满足条件:按升序排序后,与原来的序列不完全相同。问最长多长?

思路

若要满足条件,则子序列中一定要不完全升序,就是有一个元素小于前面的元素,所以若数组不是升序,那么包含那个小于前面元素的元素的任何子序列都是好的,则最大就为n;若原数组就已经是升序排序了,那么就找不出满足条件的子序列,就为0。

代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main(void)
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int n;cin >> n;
    ll a[n + 9];
    bool ok = 0;
    for(int i = 1;i <= n;i ++) cin >> a[i];
    for(int i = 2;i <= n;i ++)
    {
        if(a[i] < a[i - 1]) ok = 1;
    }
    if(ok) cout << n << '\n';
    else cout << 0 << '\n';
    return 0;
}

C. 小苯的数字合并

题意

给定一数组,你可以进行合并操作:将相邻元素合二为一,结果为两数之和。要求你最大化极差,求出最大极差。(数组的极差定义为:数组中的最大值和最小值的差。)

思路:前缀和遍历

算出前缀和,先从右往左遍历一遍,我们进行一个 ans = max(abs(pre[i] - a[i + 1]),ans); 操作;我们不难发现要使极差最大,就要拿一个合并了很多次的大数减一个未进行操作的数,不严谨反证:不妨假设那个合并的要比未进行任何操作的大(因为我们会再从后面跑一遍),如果后一个数进行了合并操作,它一定会比原来大(因为都为正数),使极差减小。那为什么进行减a[i + 1]而不是去寻找最小项呢?分类讨论,如果a[i+1]不是后面的最小项,我们进行此操作并不会影响最终结果,因为我们最终会遍历到那个最小项的;若是,就是喽。遍历完,我们就找到正着走得到的最大极差。

另一边,我们算出后缀,再从左往右跑一遍上述类似操作,就完满了。

就得出ans,输出即可。

代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll a[200005],pre[200005],rpre[200005];
int main(void)
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int n;cin >> n;

    ll ans = 0;
    for(int i = 1;i <= n;i ++) cin >> a[i];
    for(int i = 1;i <= n;i ++) pre[i] = pre[i - 1] + a[i];
    rpre[n + 1] = 0;
    for(int i = n;i >= 1;i --) rpre[i] = rpre[i + 1] + a[i];
    for(int i = 1;i < n;i ++)
        ans = max(abs(pre[i] - a[i + 1]),ans);
    for(int i = n;i > 1;i --)
        ans = max(abs(rpre[i] - a[i - 1]),ans);
    cout << ans << '\n';
    
    return 0;
}

D. 小苯的排列构造

题意

有一个长度为 n 的排列 p,其中:a[i]=gcd(p[1],p[2],...,p[i]),也就是说,ai 表示排列 p 中前i 个数字的最大公约数。已知数组a,要你构造出p排列,若无法构造,输出-1

思路

关键:构造

我们很容易观察到,相邻的两个a中后一个必须是前一个的因数,否则无法构造。另外,我们还会认识到,a[1] == q[1]。

然后,我们如何应对出现相同的a[i]呢?我们或可以按照这样一种构造方式:(我们遍历时,记录其出现次数)若这个a[i]未出现过,那它就可以成为我们所要的答案;若出现过了,我们要构造gcd相同的数就是a的倍数(从小到大找……嗯,其实都行),且之前没出现过,蒟蒻是嵌套一个while循环,注意找到的要判断是否属于排列,若不属于,就不可构造,输出-1吧;若可以,就加入答案数组中。

当最后a都是1时,我们就可以跳出循环,去寻找在1~n中仍未出现的数,加入ans数组中。

最后输出即可。

可能讲的不是很好,还请看代码理解。

代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e5 + 9;
int a[N + 9]; // a数组
int c[N + 9]; // 出现次数
vector<int> ans; // 答案
int main(void)
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int n;cin >> n;
    bool ok = 0; // 能否构造 1表否
    for(int i = 1;i <= n;i ++)
    {
        cin >> a[i]; // input
        if(i > 1) if(a[i - 1] % a[i] != 0) ok = 1; // 若后一个不是前一个的因子,就无法构造
    }
    if(ok) cout << -1 << '\n'; // 无法构造,就直接输出吧
    else // 可能能构造
    {
        for(int i = 1;i <= n;i ++)
        {
            if(!c[a[i]]) // 未出现过
            {
                ans.push_back(a[i]); // push入答案里
                c[a[i]] ++; // 出现次数++
            }
            else // 出现过
            {
                if(a[i] == 1) break; // 出现过且a[i] == 1,则break
                c[a[i]] ++; // 出现次数++
                int an = a[i] * c[a[i]]; // 因为出现过,所以要找它的倍数
                while(c[an]) // 一直找到没出现过的倍数
                {
                    c[a[i]] ++;
                    an = a[i] * c[a[i]];
                }
                if(an > n) {cout << -1 << '\n';return 0;} // 如果找到的倍数大于n,说明不存在,既无法构造
                ans.push_back(an); // 若存在,就push进答案
                c[an] ++; // 出现次数++
            }
        }
        for(int i = 1;i <= n;i ++)
            if(!c[i]) ans.push_back(i); // 找排列中其他未出现过的push进ans
        // output
        for(auto &i : ans) cout << i << ' ';
    }
    return 0;
}

E. 小苯的01背包(easy)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e3 + 9;
int v[N],w[N];
// 不同于一般背包,你选的越多,价值更高,重量更多
// 而这里是反过来的,因为&运算的性质,选的越多,价值重量越小、
// 我们可能会思考dp greedy但行不通,在n^2的范围下,还能有什么方法呢?想到枚举答案。
// 价值w二进制表示,只要某一位有1就选,某一位为0就不选
// 最大价值也只有2000,所以枚举答案ans 0~2000
void solve()
{
	int n,k,ans = 0;cin >> n >> k;
	For(int i = 1;i <= n;i ++) cin >> v[i] >> w[i];
	for(int i = 2000;i >= 0;i --)
	{
		int v_sum = (1 << 30) - 1;
		for(int j = 1;j <= n;j ++)
		{
			if((w[j] & i) == i) v_sum &= v[j];
		}
		if(v_sum <= k) ans = max(ans,i);
	}
	cout << ans << '\n';
}
int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int _ = 1;
	while(_ --) solve();
	return 0;
}

F. 小苯的01背包(hard)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e5 + 9;
ll v[N],w[N];

void solve()
{
	ll n,k,ans = 0;cin >> n >> k;
	for(int i = 1;i <= n;i ++) cin >> v[i] >> w[i];
	for(int i = 30;i >= 0;i --)
	{
		ll temp = ans | (1ll << i);
		ll v_sum = (1ll << 40) - 1ll;
		for(int j = 1;j <= n;j ++)
		{
			if((w[j] & temp) == temp) v_sum &= v[j];
		}
		if(v_sum <= k) ans = temp;
	}
	cout << ans << '\n';
}
int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int _ = 1;
	while(_ --) solve();
	return 0;
}

如果有所帮助,还请点点关注、给个赞吧!蒟蒻爱你们!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值