【通俗易懂】【2021年四川省大学生程序设计竞赛的个人题解】

概要

2021年四川省大学生程序设计竞赛的部分题解

记录:笔主现在大一下,这是笔主写的第一篇博客,文笔可能不够好,有什么建议欢迎在评论区给出。

在2025/5/17的13:00~18:00跟陈学长和林学长vp了一场2021年四川CPC,总共十道题,ac了7道,代码手主要是学长,我负责提供思路和看着学长写代码,检查错误。

题解按照难度排序给出

A题:Chuanpai

题目:
在这里插入图片描述
思路:签到题,因为数据范围很小,所以我们可以直接枚举,注意2+3和3+2这种左右数字对调的是同一种情况

#include<iostream>
#include<cstring>
using namespace std;
#define int long long
#define endl '\n'
int vis[10]; 		//记录当前数字是否被访问过,0则为未访问,1则为访问过
@author:青衫码上行
void solve() {
	memset(vis, 0, sizeof(vis));
	int x;
	cin >> x;
	int num = 0;
	for (int i = 1; i <= 6; i++) {
		int xx = x - i;
		if (xx >= 1 && xx <= 6 && !vis[xx] && !vis[i]) {
			num++;
			vis[xx] = 1;
			vis[i] = 1;
		}
	}
	cout << num << endl;

}

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

K题:K-skip Permutation

题目:
在这里插入图片描述

思路:一道构造题,构造一个满足An+k=A(n+1)最大数量的n的排列。易知符合条件子序列的肯定是单调增的,对于题目的例子n=7,k=3,我们其实可以构造1 4 7 2 5 3 6,发现了吗,1 4 7和2 5和3 6,也就是可以遍历所有的数,开一个vis数组记录是否已经跑过,对一个没跑过的数直接按要求不断加k并记录vis直到超出n为止,重复此操作直到全都跑完

#include<iostream>
#include<cstring>
using namespace std;
#define int long long
#define endl '\n'

@author:青衫码上行

int vis[1001000];	//用于标记

void solve() {
	int n, k;
	cin >> n >> k;
	int cnt = 0;
	for (int i = 1; i <= n; i++) {
		if (vis[i])
			continue;
		for (int j = i; j <= n; j += k) {
			cout << j;
			cnt++;
			vis[j] = 1;
			if (cnt != n)
				cout << " ";
		}
	}
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int t;
	t = 1;
	//cin >> t;
	while (t--) {
		solve();
	}
	return 0;
}

H题:Nihongo wa Muzukashii Desu

题目:
在这里插入图片描述

思路:这道题,乍一看感觉非常恶心,又臭又长的题目,一般来说解题思路可能比较简单。耐心读完后,发现果不其然,就是一道字符串模拟题,按照要求模拟即可,读题要仔细,小心看漏要求

#include<iostream>
#include<string>

#include<vector>
#include<algorithm>
//#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'

string s1[2] = { "chimasu","rimasu" };	//tte
string s2[3] = { "mimasu","bimasu","nimasu" };	//nde
string s3 = "kimasu";	//ite   注意:ikimasu 不适用此规则-->变为itte
string s4 = "gimasu";	//ide
string s5 = "shimasu";	//shite  

@author:青衫码上行
void solve() {
	string s;
	cin >> s;

	//s1[2]
	for (int i = 0; i < 2; i++) {
		int size = s1[i].size();
		if (s.size() < size)
			continue;
		string ss;
		for (int j = s.size() - size; j < s.size(); j++)
			ss += s[j];
		if (ss == s1[i]) {
			s.erase(s.size() - size, s.size());
			s += "tte";
			cout << s << endl;
			return;
		}
	}

	//s2[3]
	for (int i = 0; i < 3; i++) {
		int size = s2[i].size();
		if (s.size() < size)
			continue;
		string ss;
		for (int j = s.size() - size; j < s.size(); j++)
			ss += s[j];
		if (ss == s2[i]) {
			s.erase(s.size() - size, s.size());
			s += "nde";
			cout << s << endl;
			return;
		}
	}
	//s3
	int size = s3.size();
	if (s == "ikimasu") {
		cout << "itte" << endl;
		return;
	}
	if (s.size() >= size) {
		string ss;
		for (int j = s.size() - size; j < s.size(); j++)
			ss += s[j];
		if (ss == s3) {
			s.erase(s.size() - size, s.size());
			s += "ite";
			cout << s << endl;
			return;
		}
	}
	//s4
	size = s4.size();
	if (s.size() >= size) {
		string ss;
		for (int j = s.size() - size; j < s.size(); j++)
			ss += s[j];
		if (ss == s4) {
			s.erase(s.size() - size, s.size());
			s += "ide";
			cout << s << endl;
			return;
		}
	}

	//s5
	size = s5.size();
	if (s.size() >= size) {
		string ss;
		for (int j = s.size() - size; j < s.size(); j++)
			ss += s[j];
		if (ss == s5) {
			s.erase(s.size() - size, s.size());
			s += "shite";
			cout << s << endl;
			return;
		}
	}
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int t;
	cin >> t;
	while (t--) {
		solve();
	}
	return 0;
}

M题:True Story

题目:
在这里插入图片描述

思路:一开始看到题目,下意识想要二重循环模拟写出来,但发现时间复杂度会达到10的10次方,必定会超时,结合n为10的五次方,那么正解应该是时间复杂度为nlog(n)或者n级别,再仔细挖掘题意后,发现如果剩余时间无法到达机场那就不会动,只有时间足够达到机场才会移动,也就是说:只要能移动,那肯定就可以到达机场,那我们找出最大的时间段再跟每个人到达机场的所需要时间比较大小即可 时间复杂度为O(n)

#include<iostream>
//#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'

int t[100100], p[100100], v[100100];
double need[100100];
@author:青衫码上行
void solve() {
	int n, k, x, t0;
	cin >> n >> k >> x >> t0;
	for (int i = 1; i <= n; i++) {
		cin >> v[i];
		need[i] = (x * 1.0) / v[i];
	}
	for (int i = 1; i <= k; i++)
		cin >> t[i];
	for (int i = 1; i <= k; i++)
		cin >> p[i];
	int  maxtime = t0;
	for (int i = 1; i <= k; i++)
		maxtime = max(maxtime, p[i] - t[i]);
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		if (need[i] <= maxtime * 1.0)
			ans++;
	}
	cout << ans;
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int t;
	//cin >> t;
	t = 1;
	while (t--) {
		solve();
	}	return 0;
}

D题:Rock Paper Scissors

题目:
在这里插入图片描述

思路: 看着题目好像很玄奥,第二个人要实行最大化策略,第一个人要实行最小化策略,但是手推一下发现第一个人的选择其实不重要,重要的是第二个人的选择,因为无论第一个人选了什么,第二个人都会按照最大化得分策略去选择,那我们按照最大化得分去匹配模拟就行
学习学长的代码,我写的代码太多判断语句了,就不贴我的出来了

#include <iostream>
#include <algorithm>
using namespace std;
#define int long long
@author:青衫码上行
void solve()
{
	int br, bp, bs, dr, dp, ds;
	cin >> br >> bp >> bs >> dr >> dp >> ds;
	int ans = 0;
	if (dr && bs) {
		int t = min(dr, bs);
		dr -= t;
		bs -= t;
		ans += t;
	}
	if (dp && br) {
		int t = min(dp, br);
		dp -= t;
		br -= t;
		ans += t;
	}
	if (ds && bp) {
		int t = min(ds, bp);
		ds -= t;
		bp -= t;
		ans += t;
	}
	if (br && dr) {
		int t = min(br, dr);
		br -= t;
		dr -= t;
	}
	if (bp && dp) {
		int t = min(bp, dp);
		bp -= t;
		dp -= t;
	}
	if (bs && ds) {
		int t = min(bs, ds);
		bs -= t;
		ds -= t;
	}
	ans -= dr;
	ans -= dp;
	ans -= ds;
	cout << ans << endl;
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int t;
	cin >> t;
	while (t--) {
		solve();
	}
	return 0;
}

B题:Hotpot

题目:
在这里插入图片描述

思路: 因为t是10的3次方,而n是10的5次方,m又是10的9次方,时间限制为1s,所以必然不可能是按照题意直接暴力模拟,全部都跑一遍,这样必然会超时,所以我们得思考更优化的解法
1.注意到行动2n步后一定会回到初始状态,因为行动n步的话可能还有些食物是奇数1,就是没被选择,但是要是跑2n遍,则在第一个n为奇数的食物,在第二个n必然会变成偶数。
2.那我们可以先求一下m/2n为多少轮,若m/2n>0,那我们可以先模拟2n个人操作的结果,再对每个人的得分都乘以这个轮次即可。
对于最后一段不完整的行动,单独模拟。
3.这样对于一种食物来说,时间复杂度是和喜欢这种食物的人数线性相关的。 总时间复杂度O(n)
温馨提示:这里有个大坑,题目要求最后一个数据不得输出多余空格,不然答案会被认为是错误的!读题仔细仔细再仔细

#include<iostream>
#include<cstring>
using namespace std;
#define int long long
#define endl '\n'
@author:青衫码上行
int a[100100],vis[100100],need[100100];
int ans[100100];

void solve() {
	memset(vis, 0, sizeof(vis));
	memset(ans, 0, sizeof(ans));
	int n, k, m;
	cin >> n >> k >> m;
	for (int i = 0; i < n; i++) {
		cin >> a[i];
		need[i] = a[i];  //每个人喜欢的食物种类
	}
	int len = m / (2 * n);  //有多少个2n轮次
	int yu = m % (2 * n);	//跑完2n轮次后,剩余多少
	if (len > 0) {
		int index = 0;
		for (int i = 0; i < 2 * n; i++, index++) {
			if (index == n)		//注意当跑到第二个n时,处理下标,不然会越界
				index = 0;
			int pos = need[index];
			vis[pos]++;		//这里用vis数组记录次数,偶数则证明该食物存在且可以被清空,奇数则证明是新加的食物
			if (vis[pos] % 2 == 0 && vis[pos] != 0) {
				ans[index]++;
			}
		}
		for (int i = 0; i < n; i++)
			ans[i] *= len;
	}
	//跑去掉2n轮后余下的,模拟即可,注意处理下标不要让数组越界了
	int index = 0;
	for (int i = 0; i < yu; i++, index++) {
		if (index == n)
			index = 0;
		int pos = need[index];
		vis[pos]++;
		if (vis[pos] % 2 == 0 && vis[pos] != 0) {
			ans[index]++;
		}
	}
	for (int i = 0; i < n; i++) {
		cout << ans[i];
		if (i != n - 1)		//这里有个大坑,题目要求最后一个数据不得输出多余空格,不然答案会被认为是错误的!!!
			cout << " ";
	}
	cout << endl;

}

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

L题:Spicy Restaurant

题目:
在这里插入图片描述

这道题是我们过的最后一道题,图论题,也思考了很久,一开始想的是直接bfs,边跑边减枝优化,但还是TLE了,最后发现辣度值为0-100,觉得可以以这个为突破口进行bfs,这样可以很好的控制时间街复杂度
代码看着可能比较简洁,但主要是思考出来正确的解法这一步非常考验你的功力,膜拜陈学长

#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#define int long long
#define endl '\n'
using namespace std;
int ans[150][100500];
int w[100500];
int n, m;
int qq;
vector<int>arr[100500];
queue<int>q;

@author:青衫码上行

void bfs(int d) {
	for (int i = 1; i <= n;i++) {
		if (w[i] <= d) {
			ans[d][i] = 0;
			q.push(i);
		}
	}
	while (!q.empty()) {
		int temp = q.front();
		q.pop();
		for (int i = 0; i < arr[temp].size(); i++) {
			int v = arr[temp][i];
			if (ans[d][v] > ans[d][temp] + 1) {
				ans[d][v] = ans[d][temp] + 1;
				q.push(v);
			}
		}
	}
}
void solve() {
	cin >> n >> m >> qq;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= 100; j++) {
			ans[j][i] = 1e9;
		}
	}
	for (int i = 1; i <= n; i++) {
		cin >> w[i];
	}
	for (int i = 1; i <= m; i++) {
		int u, v;
		cin >> u >> v;
		arr[u].push_back(v);
		arr[v].push_back(u);
	}
	for (int i = 1; i <= 100; i++) {
		bfs(i);
	}
	for (int i = 1; i <= qq; i++) {
		int p, a;
		cin >> p >> a;
		if (ans[a][p] == 1e9) {
			cout << -1 << endl;
			continue;
		}
		cout << ans[a][p] << endl;
	}
}
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	solve();
}

最后,借助M题的一句话互勉:
not all efforts result in success, but giving up is sure to result in failure
翻译:不是所有的努力都会成功,但放弃一定会失败

如果我的内容对你有帮助,请 点赞 评论 收藏 。创作不易,大家的支持就是我坚持下去的动力!
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值