算法沉淀第七天(AtCoder Beginner Contest 428 和 小训练赛)

目录

引言:

[AtCoder Beginner Contest 428](#AtCoder Beginner Contest 428)

[Grandma's Footsteps](#Grandma's Footsteps)

题目分析

逻辑梳理

代码实现

[Most Frequent Substrings](#Most Frequent Substrings)

题目分析

逻辑梳理

代码实现

[Brackets Stack Query](#Brackets Stack Query)

题目分析

逻辑梳理

代码实现

小训练赛

货仓选址

题目分析

逻辑梳理

代码实现

[Epic Transformation](#Epic Transformation)

题目分析

逻辑梳理

代码实现

逆序对

题目分析

逻辑梳理

代码实现

结语:


引言:

今天是算法沉淀的第七天,今天我们依旧不寻找算法题来打,我们来讲解一下昨天打的AtCoder Beginner Contest 428 和 小小的一场训练赛

AtCoder Beginner Contest 428这个战绩如下,另一个训练赛就不放战绩了

因为 AtCoder Beginner Contest 428简单一点,所以我们先讲这场,再将小训练赛

那么,话不多说,我们就进入今天的算法讲解--------------------->


AtCoder Beginner Contest 428

Grandma's Footsteps

题目分析

题目链接在这A - Grandma's Footsteps

不想跳转的可看下图

这题的题目意思很简单,就是给你四个数,S,A,B,X

S是速度,A是运动持续时间,B是休息时间,X是总时间

然后问你在这总时间内,能跑多远,输出值即可


逻辑梳理

这题就纯水题了,就算A+B这段时间内,他可以移动S*A的距离,然后每次跟移动完减去X就可以了,然后记得特判X小于A的时候这题就过了


代码实现

这里就直接放AC码啦

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <queue>
#include <math.h>
using namespace std;

int t;

void solve()
{
	long long ans = 0;
	int s, a, b, x;
	cin >> s >> a >> b >> x;
	while (x>0)
	{
		if (x > a)
		{
			x -= a;
			ans += a * s;
		}
		else
		{
			ans += x * s;
			x = 0;
		}
		x -= b;
	}
	cout << ans << endl;
}

int main()
{
	
		solve();
	return 0;
}

Most Frequent Substrings

题目分析

这是题目链接B - Most Frequent Substrings

不想跳转的可看下图

这题的题目意思也很简单,就是给你一个长度为N的字符串,然后问你在这个字符串中找出出现次数最多的长度为K的连续子串,然后输出次数是多少,接着再输出符合条件的子串(如果有多个就按照字典序从小到大排序)

这题意思就这么简单,那么接下来我们进入逻辑梳理环节


逻辑梳理

这题我们只需要开个结构体数组就可以了,然后结构体数组里面的元素存连续子串是什么,以及这个连续子串出现的次数就可以了

然后我们只需要把所有的连续子串找出来,然后每次找出连续字串后,判断这个子串不是不在结构体数组中已经出现过,如果出现过,就次数++,如果没出现过,就把这个子串放进结构体数组里,然后让次数变成1,

所有子串找完后,就直接对结构体数组进行排序就可以了,按照次数的出现次数排序,如果出现次数一样 ,就按字典序排序就可以了

然后最后先输出子串出现的最长次数

再输出符合条件的子串就可以了

那么逻辑梳理完了,接下来就进入代码实现环节


代码实现

这里就直接放AC码啦

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <queue>
#include <math.h>
using namespace std;

int t;

struct node
{
	int ci;
	string a;
}ans[110];

bool cmp(node A, node B)
{
	if (A.ci == B.ci)
		return A.a < B.a;
	return A.ci > B.ci;
}

void solve()
{
	int wei = 0;
	int n, k;
	string s;
	cin >> n >> k;
	cin >> s;
	for (int i = 0; i < n - k + 1; i++)
	{
		char T[110] = "0";
		for (int j = i; j < i + k; j++)
		{
			T[j - i] = s[j];

		}
		int xixi = 1;
		for (int l = 0; l < wei; l++)
		{
			int xi = 1;
			for (int j = i; j < i + k; j++)
			{
				if (s[j] != ans[l].a[j - i])
				{
					xi = 0;
					break;
				}
			}
			if (xi)
			{
				ans[l].ci++;
				xixi = 0;
				break;
			}
		}
		if (xixi)
		{
			ans[wei].ci = 1;
			ans[wei++].a = T;
		}
	}
	sort(ans, ans + wei, cmp);
	cout << ans[0].ci << endl;
	for (int i = 0; i < wei; i++)
	{
		if (ans[i].ci != ans[0].ci)
			break;
		cout << ans[i].a << " ";
	}
	cout << endl;
}

int main()
{
	
		solve();
	return 0;
}

Brackets Stack Query

题目分析

这是题目链接C - Brackets Stack Query

不想跳转的可看下图

这题就是给你Q次操作,每次操作分为俩种

  1. 1 c

c代表( 或 ),1的操作就是在队列末尾再放一个括号

  1. 2

2就是把队列末的括号删掉

然后每次操作都要输出是YES还是NO

YES就是括号都是匹配的

NO就是括号不匹配

那么,这题题目就分析完啦,接下来,我们进入逻辑梳理环节


逻辑梳理

首先,最简单的,我们可以记录左括号和右括号的个数 ,然后,每次操作完后,我们对左括号和右括号的个数进行比较,如果一样多,就输出YES,反之则输出NO

那么,在这种情况下,大部分的情况都是能过的,但是也有特殊情况

因为括号肯定要左括号和右括号配对,尽管左右括号的个数一样多,但如果右括号出现时,左括号没有能与之配对,那就依旧输出NO

所以我们可以用一个数组,来存在哪一个地方会使得右括号比左括号多,如果右括号比左括号多了,不管后面怎么操作,都是NO,所以标记完这个位置后,直到把这个位置的括号删掉后,才可以接着判断YES或者NO

那么这题的逻辑梳理就梳理完啦,接下来,我们进入代码实现环节


代码实现

这里就直接放代码啦

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <queue>
#include <math.h>
using namespace std;

int t;
char a[800010];
int r = 0;
int l = 0;
int wei = 1;
int pan = 0;
void solve()
{
	int c;
	cin >> c;
	if (c == 1)
	{
		cin >> a[wei];
		if (a[wei] == '(')
			l++;
		else
			r++;
		wei++;
		if (r > l && pan == 0)
			pan = wei;

	}
	else
	{
		if (a[--wei] == '(')
			l--;
		else
			r--;
		if (wei + 1 == pan)
			pan = 0;
	}
	if (l == r)
	{
		if (pan != 0)
			cout << "No" << endl;
		else
			cout << "Yes" << endl;
	}
	else
		cout << "No" << endl;
}

int main()
{
	cin >> t;
	while (t--)
		solve();
	return 0;
}

那么这场比赛的题就讲完啦,接下来我们来看小训练赛的题


小训练赛

这场就直接放题目图啦

货仓选址

题目分析

这题的题目意思很简单,就是给你几个商店的位置,每个位置相当于是在x轴的位置

然后题目让你在x轴上选一个位置作为仓库,然后这个点和所有商店的距离加起来要最小

输出加起来最小的那个值就可以啦

那么,题目就分析完啦,接下来我们进入逻辑梳理环节


逻辑梳理

这题因为要选位置,那乱序的数肯定不好选,所以,我们首先把这些数排序完,再进行操作

那么接下来我们来讨论商店的个数,假设商店个数为n

如果商店的个数是奇数,我们把仓库的位置选在第(n+1)/2个商店的位置就是和最小的时候

如果商店的个数是偶数,我们把仓库的位置选在第n/2和第n/2+1个商店位置之间的位置就是和最小的时候

那么,逻辑梳理完啦,接下来就进入代码实现环节


代码实现

这里就直接放AC码啦

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <queue>
#include <math.h>
using namespace std;

int n;
int a[100010];

int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
	}
	sort(a + 1, a + n + 1);
	long long ans = 0;
	for (int i = 1; i <= n / 2; i++)
	{
		ans += a[n - i + 1] - a[i];
	}
	cout << ans << endl;
	return 0;
}

Epic Transformation

题目分析

这题的意思就是给你一个数组,然后每次都可以删掉2个不同的数,直到不能删为止,然后问最少可以剩下几个元素

这题题目意思就这么简单,接下来我们进入逻辑梳理环节


逻辑梳理

这题我们可以先找出出现最多次的元素的次数

1.如果出现次数最多的元素次数>=剩余元素个数

那就输出这个元素的个数-剩余元素个数

2.如果出现次数最多的元素次数<剩余元素个数

2.1 如果是奇数情况,就输出1

2.2 如果是偶数情况,就输出0

那么,这题的逻辑就梳理完啦,接下来就进入代码实现环节


代码实现

这里就直接放代码啦

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <queue>
#include <math.h>
using namespace std;

int t;
long long a[200010];

void solve()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	sort(a + 1, a + n + 1);
	int zui = 1;
	int k = 1;
	for (int i = 2; i <= n; i++)
	{
		if (a[i] == a[i - 1])
			k++;
		else
		{
			zui = max(zui, k);
			k = 1;
		}
	}
	zui = max(zui, k);
	if (n % 2)
	{
		if (zui > n - zui)
			cout << n - (n - zui) - (n - zui) << endl;
		else
			cout << "1" << endl;
	}
	else
	{
		if (zui > n - zui)
			cout << n - (n - zui) - (n - zui) << endl;
		else
			cout << "0" << endl;
	}
	return;
}

int main()
{
	cin >> t;
	while (t--)
	{
		solve();
	}
	return 0;
}

逆序对

题目分析

这题的要求也很简单,给你一个长度为n的数组,让你找出这个数组中有几对逆序对,输出就好了

那么,接下来就进入逻辑梳理环节


逻辑梳理

这题直接暴力肯定是行不通的,那么我们可以想想有什么算法是可以找逆序对的呢?

对,就是归并排序,归并排序中,有这么一串核心代码

cpp 复制代码
while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] <= a[begin2])
		{
			b[j++] = a[begin1++];
		}
		else
		{
			ans += mid - begin1 + 1;
			b[j++] = a[begin2++];
		}
	}

这是看哪个小放那个,那么,当a[begin1]>a[begin2]时,从begin1开始的前面那部分都可以和a[begin2]的元素组成逆序对,所以只需要在else语句中加上迭代的表达式就可以了

归并排序完成之时,就是逆序对个数求出之时

那么这题的逻辑梳理就梳理完啦,接下来进入代码实现环节


代码实现

这里就直接放代码啦

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <queue>
#include <math.h>
using namespace std;

long long ans = 0;
long long a[500010];
long long b[500010];

void guibing(int left, int right)
{
	if (left >= right)
		return;
	int mid = (left + right) / 2;
	int begin1 = left;
	int begin2 = mid + 1;
	int end1 = mid;
	int end2 = right;
	int j = 0;
	guibing(left, mid);
	guibing(mid + 1, right);
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] <= a[begin2])
		{
			b[j++] = a[begin1++];
		}
		else
		{
			ans += mid - begin1 + 1;
			b[j++] = a[begin2++];
		}
	}
	while (begin1 <= end1)
		b[j++] = a[begin1++];
	while (begin2 <= end2)
		b[j++] = a[begin2++];
	for (int i = left; i <= right; i++)
		a[i] = b[i - left];
}

void solve()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	guibing(1, n);
	cout << ans << endl;
}

int main()
{
	solve();
	return 0;
}

结语:

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

相关推荐
前端小刘哥4 小时前
新版视频直播点播平台EasyDSS,打通远程教研与教师培训新通路
算法
2401_840105204 小时前
P1049 装箱问题 题解(四种方法)附DP和DFS的对比
c++·算法·深度优先·动态规划
kobe_t4 小时前
数据安全系列7:常用的非对称算法浅析
算法
靠近彗星4 小时前
3.4特殊矩阵的压缩存储
数据结构·人工智能·算法
清辞8535 小时前
C++入门(底层知识C与C++的不同)
开发语言·c++·算法
fqbqrr5 小时前
2510C++,api设计原则,不除零
开发语言·c++
~kiss~5 小时前
图像处理~多尺度边缘检测算法
图像处理·算法·计算机视觉
fqbqrr5 小时前
2510d,C++虚混杂
c++·d
Mr.看海5 小时前
机器学习鼻祖级算法——使用SVM实现多分类及Python实现
算法·机器学习·支持向量机