牛客小白月赛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;
}
如果有所帮助,还请点点关注、给个赞吧!蒟蒻爱你们!