算法沉淀第八天(牛客周赛 Round 114 和 Incremental Stay) - 详解
目录
引言:
今天是算法沉淀的第八天,因为昨天只打了一场比赛,所以今天总结完有多余时间再开一道CF1400的题
昨天的牛客周赛 Round 114的战绩如下

之后题目链接就不放啦,就直接放题目的图啦
那么,话不多说,我们就进入今天的算法讲解———————>

牛客周赛 Round 114
小彩找数
题目分析
这题就是问你1,2,3这三个数分别出现在数组的哪个位置,然后输出位置就好了
逻辑梳理
这纯水题,就循环3次每次都找一个数就好了
代码实现
这里就直接放代码啦
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
#include
#include
using namespace std;
void solve()
{
int a[4];
for (int i = 1; i <= 3; i++)
cin >> a[i];
for (int i = 1; i <= 3; i++)
{
for (int j = 1; j <= 3; j++)
{
if (a[j] == i)
{
cout << j << " ";
continue;
}
}
}
}
int main()
{
solve();
return 0;
}
小彩的好字符串
题目分析
这题就是给你一个长度为n的字符串,然后问你这个字符串中有几个连续子串满足既存在1,也存在2,也存在3,并且1,2,3的个数一样
输出满足条件的连续子串个数就可以了
逻辑梳理
这题因为数据范围给的很小,所以开俩层循环把所有的连续字串都分析时间复杂度都不会超
那么在循环前,我们可以用前缀和对字符串先进行预处理,分别算出1,2,3出现次数的前缀和。
然后在判断子串的时候只要通过1,2,3的前缀和数组来判断是不是满足条件就可以了
然后循环结束后把结果输出就可以啦
逻辑梳理完了,接下来我们来看代码
代码实现
这里就直接放源码啦
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int yi[2010];
int er[2010];
int san[2010];
void solve()
{
int n;
cin >> n;
string a;
cin >> a;
for (int i = 0; i < n; i++)
{
if (a[i] == '1')
{
yi[i + 1] = yi[i] + 1;
er[i + 1] = er[i];
san[i + 1] = san[i];
}
if (a[i] == '2')
{
yi[i + 1] = yi[i];
er[i + 1] = er[i]+1;
san[i + 1] = san[i];
}
if (a[i] == '3')
{
yi[i + 1] = yi[i];
er[i + 1] = er[i];
san[i + 1] = san[i]+1;
}
}
long long ans = 0;
for (int i = 3; i <= n; i++)
{
for (int j = 1; j <= i; j++)
{
if (yi[i] - yi[j - 1] == er[i] - er[j - 1] && er[i] - er[j - 1] == san[i] - san[j - 1])
{
ans++;
}
}
}
cout << ans << endl;
}
int main()
{
solve();
return 0;
}
小彩的字符串交换
题目分析

这题就是给你一个字符串,然后你可以选择任意俩个下标,交换这俩个下标对应的值,然后需要在操作n次后,字符串中存在长为3的连续子串,且该子串中有1,有2,也有3
如果可以得到满足条件的连续子串,就输出最少的操作次数
如果得不到满足条件的连续子串,就输出-1;
那么题目分析完了,接下来就进入逻辑梳理环节
逻辑梳理
我们可以先看输出-1的情况,什么情况下会输出-1呢,就是1,2,3这三个有一个及以上没有出现在字符串里
如果字符串里,1,2,3都出现过,那肯定是能操作出满足条件的子串的
那么怎么操作呢,我们从1开始统计长度为3的子串中元素情况,如果都一样,就是要换2次,如果有一个不一样就是要换1一次,如果都不一样,就是不用换,这个时候直接结束循环就好了
那么,逻辑梳理完了,接下来我们就进入代码实现环节
代码实现
这里就直接放代码啦
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int t;
void solve()
{
bool vis[4] = { 0 };
int n;
string a;
cin >> n;
cin >> a;
int butong = 0;
for (int i = 0; i < n; i++)
{
vis[a[i] - '0'] = 1;
if (i >= 2)
{
if (a[i] == a[i - 1] && a[i] == a[i - 2] && a[i - 1] == a[i - 2])
{
butong = max(butong, 1);
}
else if (a[i] == a[i - 1] || a[i] == a[i - 2] || a[i - 1] == a[i - 2])
{
butong = max(butong, 2);
}
else
{
butong = 3;
break;
}
}
}
if (vis[1] + vis[2] + vis[3] != 3)
{
cout << "-1" << endl;
return;
}
cout << 3 - butong << endl;
return;
}
int main()
{
cin >> t;
while (t--)
solve();
return 0;
}
小彩的数组选数
题目分析

这题就是给你一个数组,然后你每次可以选择一个位置,得分加上这个位置的元素,然后将这个位置以及相邻位置的元素变成0,直到数组全部变成0
问你得分最高可以是多少,输出最高可能得分就可以了
那么题目分析完了,接下来我们进入代码实现环节
逻辑梳理
这题我们直接从整个数组来看很难想到怎么找,那么我们就来把数组从小到大扩大,看看能不能找出一点规律
首先,如果数组长度为1,那肯定得分最多就是a[1]
那么到数组长度为2的时候,得分最多肯定就是max(a[1],a[2]),因为不管取哪个,另一个都会变成0(相邻)
那数组长度为3时候,就会有俩种情况了,就是max(a[1]+a[3],a[2])
因为如果取1和3位置的元素,影响的只是2位置的元素变成0,而如果取了2位置的元素,那么1和3位置的元素就都变成0了,所以就有了这个式子
推到这,基本就已经可以知道了一个迭代的式子
我们先设个数组ans,用来存长度不同时所能达到的最大值
首先把ans[1]和ans[2]初始化成a[1]和max(a[1],a[2]),然后从3开始循环,进行迭代
ans[i]=max(ans[i-1],ans[i-2]+a[i])
为什么是这个式子呢,ans[i-1]是确保从0到i-1的数组全是0的时候都最大得分,ans[i-2]是确保从0到i-2的数组全是0的时候最大得分情况,因为i-1位置选与不选会影响到i位置的元素是否存在,而i-2位置元素的选与不选并不会影响到i的情况,所以会加上a[i]的值
如果i-1位置不选的话,那ans[i-1]和ans[i-2]就一样了,所以不会影响ans[i]的最终值
如果i-1位置选的话,那i位置肯定变成0了,所以也不用加a[i]。
据此,这个式子就成立了
只需要一直迭代直到循环结束,输出ans[n]就可以了
那么逻辑梳理完了,接下来我们就进入代码实现环节
代码实现
这里就直接放代码啦
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int a[200010];
long long ans[200010];
void solve()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
if (n == 1)
{
cout << a[1] << endl;
return;
}
ans[1] = a[1];
ans[2] = max(a[1], a[2]);
for (int i = 3; i <= n; i++)
{
ans[i] = max(ans[i - 2] + a[i], ans[i - 1]);
}
cout << ans[n] << endl;
}
int main()
{
solve();
return 0;
}
到此,牛客周赛的题目就讲完啦,接下来我们来看一道CF1400的题
Incremental Stay
题目分析


这题意思就是给你一个数n表示有多少人进出博物馆,然后再输入2×n个数,这些数表示在这个时间,有人经过了博物馆门(是进是出不知道)
然后输出博物馆容纳不同人数(1——n)时,输出所有人呆在博物馆时间加起来的最大值就可以了
那么题目就分析完了,接下来我们进入逻辑梳理环节
逻辑梳理
首先我们先来博物馆只能容纳一个人的情况,这时候只有进出进出进出的情况
然后我们再来看能容纳俩个人的情况,这情况下只需要让1个人进去后就一直待到最后再走,然后另一个进出进出进出,就能得到时间最大值了
然后我们再来看容纳三个人的情况,那么只要先让一个人进去待到最后走,那么又变成了俩个人的情况。以此类推,所有的情况就都适配了
所以如果是容纳n个人的时候,就先让n-1个人依次进去,然后待到最后依次出来,然后剩余的一个人就进出进出进出就好了
那么这题的逻辑就梳理完啦,接下来我们进入代码实现环节
代码实现
首先是进出进出的表示,如果每次都算的话大概率是会超时,那么我们可以先用前缀和数组把数据先预处理完,因为分为从奇数下标进和偶数下标进,所以需要俩个前缀和来分别表示奇数进和偶数进的时候
然后最里面的人的时间处理完了,接下来我们要处理剩余n-1个人的时间
因为第一次是最边边俩个相加,第二次是第二层相加,第三次是第三层相加,所以我们可以在循环的时候直接迭代就可以了,这个具体讲比较讲不清楚,直接看代码吧,代码表示的还是很好理解的
前缀和版本
那么,接下来就直接放AC码啦
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int t;
long long a[400010];
long long ji[200010];
long long ou[200010];
void solve()
{
int n;
cin >> n;
for (int i = 1; i <= 2 * n; i++)
{
cin >> a[i];
}
for (int i = 1, j = 1; i <= n; i++, j += 2)
{
ji[i] = ji[i - 1] + a[j + 1] - a[j];
}
for (int i = 1, j = 2; i < n; i++, j += 2)
{
ou[i] = ou[i - 1] + a[j + 1] - a[j];
}
long long ans = 0;
cout << ji[n] << " ";
for (int i = 2; i <= n; i++)
{
ans += a[n * 2 - i + 2] - a[i - 1];
if (i % 2)
{
cout << ans + ji[n - i / 2] - ji[i / 2] << " ";
}
else
{
cout << ans + ou[n - i / 2] - ou[i / 2 - 1] << " ";
}
}
cout << endl;
return;
}
int main()
{
cin >> t;
while (t--)
solve();
return 0;
}
但是这题其实是可以优化的,我也是今天思考了一下灵机一动发现的,优化后的代码如下,下面的这个代码就不需要用到前缀和,也少了几个循环,时间和空间复杂度都降下去了很多,具体就看下面代码
优化版本(不用算法,只用迭代)
这个代码对迭代的运用要求有点高,实际还是跟上面一个逻辑,如下
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int t;
long long a[400010];
void solve()
{
int n;
cin >> n;
long long lin = 0;
for (int i = 1; i <= 2 * n; i++)
{
cin >> a[i];
if (i % 2)
{
lin -= a[i];
}
else
{
lin += a[i];
}
}
long long ans = 0;
for (int i = 1; i <= n; i++)
{
cout << lin + ans << " ";
lin = a[2 * n - i + 1] - a[i] - lin;
ans += a[n * 2 - i + 1] - a[i];
}
cout << endl;
return;
}
int main()
{
cin >> t;
while (t--)
solve();
return 0;
}
这俩种代码的时间和空间复杂度如下图,主要是空间降得多

那么这题就讲解完啦
结语:
今日算法讲解到此结束啦,希望对你们有所帮助,谢谢观看,如果觉得不错可以分享给朋友哟。有什么看不懂的可以评论问哦,


浙公网安备 33010602011771号