目录
引言:
时隔多日,我又来沉淀算法了,今天我们来讲今晚的牛客练习赛的题目,拼尽全力,开了三题,燃尽了,如图


为什么许久没更新了呢,因为前些天在深度梳理C语言指针方面的知识,我打算之后C语言指针部分就直接用一篇博客来写了,所以在指针博客出炉前,会有一段的沉淀期
那么,闲话少叙,接下来我们 进入正式的算法讲解——————>
这场比赛的链接在这(8条未读私信) 牛客练习赛146_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ

合格的机器
按照惯例,我们先来看题目
题意分析


这个题目的意思就是给你n个机子,每个机子上都有代币,如果机子上的代币为偶数个,那么这个机子就是合格的,如果机子上的代币为奇数个,那么这个机子就是不合格的
然后,我们可以对机子进行任意次操作,每次操作都可以选择一个代币数量不低于2个的机子,从这个机子上取出一个代币,随后选择一个机子,将这个代币放到选择的机子上(也可以是本机)
然后问你进行任意次操作后,最多能让多少台机子处于合格状态,输出台数就可以了
逻辑梳理
首先,因为我们是从一堆机子里选一个机子取出代币,然后再选一个机子放入代币,所以所有机子的总代币数量是不变的
那么,我们可以把机子先进行操作,变化为每个机子的代币个数为2,然后剩余的所有代币都放到一个机子上,此时,便是合格状态最多的情况的机子了
那么,为什么可以这么变化呢,我们来看图,如下图

那么,此时,我们只需要判断k是奇数还是偶数就可以了,因为其余位置的机子都是合格的
通过图的分析,大部分情况就已经讨论完了,但有一处需要注意,那便是,总的代币数量如果没有2×n个,就无法满足n-1个元素都是2个代币,所以,我们还需要进行分类讨论
如果代币数量没有2×n个,就直接输出代币的个数取模n就可以了
如果代币数量不少于2×n个,就在n-1个机子的情况下,再判断k代币的机子是否合规就可以了
那么,逻辑梳理完了,接下来就进入代码实现环节
代码实现
这里就直接放AC码啦
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <queue>
using namespace std;
int a[200010];
void solve()
{
int n;
cin >> n;
int ans = 0;
int sum = 0;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
sum += a[i];
}
if (sum / n >= 2)
{
ans = (sum % 2 == 0) ? n : n - 1;
cout << ans << endl;
}
else
{
cout << sum % n << endl;
}
return;
}
int main()
{
solve();
return 0;
}
小灰灰的火焰星球2
题意分析


这题就是给你一个数组,下标越靠前,温度越高,也就是这个数组下标从小到大对应的温度是单调不递增的,然后这个数组每一个位置都会有一个价值,那么初始的数组就搭建完成了
接下来,就是给你T次施法机会,每次施法会给一个法力值,施法后可以选择俩个温度低于法力值的区域采集那俩快区域的价值,每次施法采集完俩次后,数组的区域价值就又会重置(即之后施法也可以采集相同区域的价值)
然后,题目问你施法T次后,所能获得的最大价值为多少
逻辑梳理
首先,因为施法顺序的改变是不会影响最后的结果的,所以,我们可以先将T次的施法机会输入进去后,先进行排序,排序成从小到大的样子,这样就可以大幅降低之后操作的时间复杂度
然后题目给我们的数组的温度是从高到低的,这样就要用到后缀和,但是这样操作就很麻烦,我是这么感觉的,所以可以在输入时候倒着输入(即下标从后往前),这样输入完这个数组的 温度就是单调不递减的了
此时,因为施法顺序已经排完序了,是从小到大的,所以下一次的施法,获得的价值是肯定不低于上一次施法获得的价值的,所以,我们只需要一次循环就可以将每次施法可以获得的最多价值给算出来,这也是为什么要对施法顺序进行排序
而具体获得多少价值,就可以通过对原数组的温度和价值进行预处理
首先,我们可以先设俩个变量进行更新迭代,假设分别为max1和max2,,一开始将这俩个变量置为0,随后循环从1到n时,每次到下一个下标时,将当前下标的价值与max1进行比较,如果比max1大,且比max2小,就将这个价值替换为max1,如果比max1大,也比max2大,就讲max2赋值给max1,随后max2赋为当前下标的价值
那么,我们再创一个装价值的数组b,每次max1和max2更新前或者更新后,当前下标的值也进行更改,当前下标所对应的值赋更新前和更新后的意义是不同的,那么,我们来讲一讲俩种情况下的意义,这俩种方式我都尝试了一下,在代码实现部分我会将这俩种更新方式的AC码都放出来
第一种是在max1和max2更新前对下标的值进行更改,此时,b数组的每个下标上的元素代表的意义是:法力值若低于这个下标的温度,且比前面下标的温度高,获得的最大价值就是这个元素
第二种是在max1和max2更新后对下标的值进行更改,此时,b数组的每个下标上的元素代表的意义是:法力值若高于这个下标的温度,且比后面下标的温度低,获得的最大价值就是这个元素
我个人是觉得第一种方式更清晰明了点,需要处理的步骤也少点
当然,这俩种情况都要进行细节处理
这两种情况下,主体代码基本大差不差,主要是要考虑极端情况,比如如果法力值比最低温度都要小,又或者法力值比最高温度还要高
将极端情况也考虑进去后,这题就没有问题了
那么逻辑梳理完了,接下来来看代码实现
代码实现
第一种AC码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <queue>
using namespace std;
int hot[100010];
int siz[100010];
int ma[100010];
int a[100010];
void solve()
{
int n, t, max1 = 0, max2 = 0;
cin >> n >> t;
for (int i = n; i >= 1; i--)
{
cin >> hot[i];
}
for (int i = n; i >= 1; i--)
{
cin >> siz[i];
}
for (int i = 1; i <= n; i++)
{
ma[i] = max1 + max2;
if (siz[i] > max1)
{
if (siz[i] > max2)
{
max1 = max2;
max2 = siz[i];
}
else
{
max1 = siz[i];
}
}
}
hot[n + 1] = 1000000;
ma[n + 1] = max1+max2;
for(int i = 1;i<=t;i++)
{
cin >> a[i];
}
sort(a + 1, a + t + 1);
int left = 1;
long long ans = 0;
for (int i = 1; i <= t; i++)
{
while (hot[left] < a[i])
{
left++;
}
ans += ma[left];
}
cout << ans << endl;
return;
}
int main()
{
solve();
return 0;
}
第二种AC码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <queue>
using namespace std;
int hot[100010];
int siz[100010];
int ma[100010];
int a[100010];
void solve()
{
int n, t, max1 = 0, max2 = 0;
cin >> n >> t;
for (int i = n; i >= 1; i--)
{
cin >> hot[i];
}
for (int i = n; i >= 1; i--)
{
cin >> siz[i];
}
for (int i = 1; i <= n; i++)
{
if (siz[i] > max1)
{
if (siz[i] > max2)
{
max1 = max2;
max2 = siz[i];
}
else
{
max1 = siz[i];
}
}
ma[i] = max1 + max2;
}
hot[n + 1] = 1000000;
ma[n + 1] = ma[n];
for(int i = 1;i<=t;i++)
{
cin >> a[i];
}
sort(a + 1, a + t + 1);
int left = 1;
long long ans = 0;
for (int i = 1; i <= t; i++)
{
while (hot[left] < a[i])
{
left++;
}
left--;
if (left < 1)
left = 0;
ans += ma[left];
}
cout << ans << endl;
return;
}
int main()
{
solve();
return 0;
}
盲目自大的小灰灰
有一说一,C题我感觉甚至比B题简单
题意分析



这题就是先给你一个时间,然后再告诉你有几个陷阱,然后每个陷阱触发的时间在什么时候,触发后玩家此时不能在哪边,0代表左边,1代表右边
随后给你t次查询,每次查询给你一个数,问你这个分数可不可能达到(得活着完成游戏),玩家每次向另一个方向跳跃,就会加一分
能就输出Yes,不能就输出No
逻辑梳理
这题逻辑也很简单,先算出最小可能得分,再算出最大可能得分,然后后面每次查询得分时,就判断得分是不是在得分区间内,如果在区间外,就输出No,如果在区间内再进行判断,然如果t时间时有障碍物,那么就直接输出Yes,反之,就判断,分-最小分能不能被2整除,能就输出Yes,不能就输出No
为什么呢,因为如果没有自由控制区间,那么,得分只会2分2分的变,如果有了自由控制区间,最小到最大的分值也就成了必然,这个画图就很容易理解了,就不过多阐述了
接下来进入代码实现环节
代码实现
这里就直接放AC码啦
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <queue>
using namespace std;
bool tim[200010];
bool wei[200010];
void solve()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int k, K;
cin >> k >> K;
tim[k] = 1;
wei[k] = K;
}
bool fang = 0;
int ma = 0;
int mi = 0;
int sui = 0;
for (int i = 1; i <= n; i++)
{
if (tim[i] && fang == wei[i])
{
fang = !fang;
mi++;
}
}
fang = 0;
for (int i = 1; i <= n; i++)
{
if (!tim[i])
{
fang = !fang;
ma++;
sui++;
}
else
{
if (fang == wei[i])
{
fang = !fang;
ma++;
}
sui = 0;
}
}
int t;
cin >> t;
while (t--)
{
int k;
cin >> k;
if (k >= mi && k <= ma)
{
if (sui != 0)
{
cout << "Yes" << endl;
}
else if ((k - mi) % 2)
{
cout << "No" << endl;
}
else
{
cout << "Yes" << endl;
}
}
else
{
cout << "No" << endl;
}
}
return;
}
int main()
{
solve();
return 0;
}
结语:
今日算法讲解到此结束啦,希望对你们有所帮助,谢谢观看,如果觉得不错可以分享给朋友哟。有什么看不懂的可以评论问哦,

2197

被折叠的 条评论
为什么被折叠?



