2024年第十五届蓝桥杯C++C组 省赛

331 阅读5分钟

蓝桥杯2024年第十五届省赛真题-数字诗意

思想

这道题按照题意做即可,每输入一个数就检查一下是否是蕴含诗意的数字。如果不是,就统计一下不是的个数。

现场code

我的第一个做法是每输入一个数字x,我就从1开始加到x(不包含x),如果相加过程中 相加和能等于x,就说明x是一个诗意数字,否则到加完为止,仍然相加和不等于x,则说明x不是一个诗意数字。

#include <bits/stdc++.h>
using namespace std;
int n, ans;

bool check(int x) {
	int sum = 0;
	for (int i = 1; i < x; i++) {
		sum += i;
		if (sum == x)
			return true;
	}
	return false;
}

int main() {
	cin >> n;
	for (int i = 0; i < n; i++) {
		int x;
		cin >> x;
		if (!check(x)) {
			ans++;
		}
	}
	cout << ans << endl;

	return 0;
}

image.png

上面的代码只能过部分样例,正解是通过我们打表发现规律:

image.png

我们发现,只有当i是2的j次方的时候,i才不是诗意数字,其他的情况i都是诗意数字。

我们发现a[i]最大只能1e16,2的54次方大于1e16,所以我们只用枚举到54即可。

这个范围很小,可以通过。

#include <bits/stdc++.h>
using namespace std;
int ans;

int main() {
	int n;
	cin >> n;
	for (int i = 0; i < n; i++) {
		int x;
		cin >> x;

		for (int j = 0; j <= 54; j++) {
			if (x == pow(2, j)) {
				ans++;
			}
		}
	}
	cout << ans << endl;
	return 0;
}

蓝桥杯2024年第十五届省赛真题-封闭图形个数

思想

这题最简单,按要求模拟即可。

code

#include <bits/stdc++.h>
using namespace std;
#define int long long 
typedef pair<int, int>PII;
int n;

int cnt(int x) {
	if (x == 0)
		return 1;
	else if (x == 1 || x == 2 || x == 3 || x == 5 || x == 7)
		return 0;
	if (x == 4)return 1;
	else if (x == 6)
		return 1;
	else if (x == 8)
		return 2;
	else if (x == 9)
		return 1;
}

int chai(int x) {
	int sum = 0;
	while (x) {
		int t = x % 10;
		sum += cnt(t);
		x /= 10;
	}
	return sum;
}

bool cmp(PII a,PII b)
{
     if(a.second==b.second)return a.first<b.first;
	return a.second < b.second;
}

signed main() {
	cin.tie(nullptr)->sync_with_stdio(0);
	cin >> n;
	vector<PII>a;
	for (int i = 0; i < n; i++) {
		int x;
		cin >> x;
		a.push_back({ x, chai(x)});
	}

	sort(a.begin(), a.end(),cmp);

	for (auto &it:a)
	{
		cout << it.first << " ";
	}
	
	return 0;
}

image.png







image.png image.png

样例输入:

4
1 2 3 4

样例输出:


3

思想 我刚开始找的最优解是把数组v1翻转一下v2,然后去与v1比较。 比如

1 2 3 4
4 3 2 1 

第一位数字1要想变为4,那么就要经过3次操作,这个数是最大的

code

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n;
int maxn;
int main()
{
		cin >> n;
		vector<int>v1(n);
		vector<int>v2(n);
		for (int i = 0; i < n; i++)
		{
			cin >> v1[i];
		}
		v2 = v1;
		reverse(v2.begin(),v2.end());
		for (int i = 0; i < n; i++)
		{
			maxn = max(maxn,v2[i]-v1[i]);
		}
	
		cout << maxn << endl;
	return 0;
}









蓝桥杯2024年第十五届省赛真题-商品库存管理

思想

给某段区间加上1,很明显的差分。所以我们可以先用差分把每段区间求出来:

我虽然实现了具体功能,但是却过不了

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 3e5 + 10;
int n, m;
int d[N];
int b[N];
int l[N], r[N];
void  qian()
{
	for (int i = 1; i <= n; i++)d[i] += d[i - 1];
}
void insert(int l,int r)
{
	d[l]++;
	d[r + 1]--;
}
void solve()
{
	cin >> n >> m;
	for (int i = 1; i <= m; i++)
	{
		 cin >> l[i] >> r[i];
	}

	for (int i = 1; i <= m; i++)
	{
		memset(d,0,sizeof d);
		for (int j = 1; j <= m; j++)
		{
			if (j != i)
			{
				insert(l[j],r[j]);
			}
		}
		qian();
		for (int i = 1; i <= n; i++)cout << d[i] << " ";
		cout << endl;
	}
}
signed main()
{
	cin.tie(nullptr)->sync_with_stdio(false);

	int t = 1;
	while (t--)
	{
		solve();
	}
	return 0;
}

image.png image.png









然后再统计一下每段区间里面0的个数即可。

就拿样例举例:

绿色代表矿,m是4,让我们找一条不超过4的可以到达最多矿的路径:

A路径:

image.png 到达的矿的个数:2

B路径:

image.png 到达的矿的个数:3

C路径: image.png 到达的矿的个数:4

我们发现路径c就是最优路径。

经过上面的打表我们可以发现一个规律,我们一刚开始可以选择往左走或者往右走,假设我们刚开始往左走。 一旦发现左侧的矿点的下标大于了m的一半,那这个矿点我们就不走了,直接往右走。

image.png 那么还有一种情况,就是说往右走,右侧的矿点的个数不如我不回头,一口气超左把m走完效益大。

蓝桥杯2024年第十五届省赛真题-回文字符串

思想

我刚开始想的是只需要去看不是'l''q''b'字符组成的子串是否是回文子串就可以判断出整个字符串是否可以拼成回文字符串。因为如果非'l''q'b'子串不是回文子串,整个串我们就不可能使其变为回文串。

然后只过了55%:

#include<bits/stdc++.h>
using namespace std;
int n;
int main()
{
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		string s; cin >> s;
		string temp = "";
		for (int i = 0; i < s.size(); i++)
		{
			if (s[i] != 'l' && s[i] != 'q' && s[i] != 'b')
			{
				temp += s[i];
			}
		}
		string rev = temp;
		reverse(rev.begin(),rev.end());
		if (temp == rev)cout << "Yes" << endl;
		else cout << "No" << endl;
		//cout << temp << endl; 
	}

	return 0;
}

image.png

修正

上面的想法没错,但是在代码实现上有些问题。

因为题目说了只能向头部插入'l','q','b',因此该串要想是一个回文串,那么必定尾部肯定是由'l','q','b'组成的子串。然后中间部分肯定是一个不包含'l,q,b'的回文子串,这样我们才可以拼成一个回文子串。

code

我们从字符串的尾部开始处理,先把包含'l,q,b'的子串过滤掉,然后找到从后向前的第一个不包含'l,q,b'的子串的字符下标pos,我们从提取一下从0开始到pos位置的子串temp,这个长度就是pos+1的长度。

然后我们就看一下提取的子串是否是回文子串就可以判断出我们是否可以把当前字符串补为一个回文串了。

#include <bits/stdc++.h>
using namespace std;

int n;

void solve() {

	string s;
	cin >> s;

	int pos = s.size() - 1;
	for (; pos >= 0; pos--) {
		if (s[pos] != 'l' && s[pos] != 'q' && s[pos] != 'b')
			break;
	}
	string temp = s.substr(0, pos + 1);

	string rev = temp;
	reverse(rev.begin(), rev.end());
	if (temp == rev)
		cout << "Yes" << endl;
	else
		cout << "No" << endl;


}

int main() {
	cin.tie(nullptr)->sync_with_stdio(0);
	int t = 1;
	cin >> t;
	while (t--) {
		solve();
	}

	return 0;
}

image.png image.png