小黄鸡小组周赛 2 新手向题解

A. Question Marks

翻译

蒂姆正在做一个由 \(4n\) 个问题组成的测试;每个问题有 \(4\) 个选项:A"、"B"、"C "和 "D"。每个选项都有 \(n\) 个正确答案,即有 \(n\) 道题的答案是 "A", \(n\) 道题的答案是 "B", \(n\) 道题的答案是 "C", \(n\) 道题的答案是 "D"。

对于每道题,蒂姆都会把答案写在答题纸上。如果想不出答案,他会在该题上留下问号"?

他的答题纸有 \(4n\) 个字符。蒂姆最多能答对多少题?

思路

题目还是很简单的,做出来的人比较少可能只是卡英文了而已。

阅读完题目可以发现:每种选项最多只有 \(n\)。举个例子,如果 \(n = 4\),而我写了 \(5\) 个 A,那么我最多只能答对 \(4\) 题,因为最多只有 \(4\) 个 A;如果我写了 \(3\) 个 A,那么我只能答对 3 题。所以,我们只需要对每个选项的个数统计一下,然后和 \(n\) 比较,取最小值即可。

代码

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;

const int N = 2e5 + 10;

const int inf = 1e9 + 7;

int n,m,k,ans;

string s;

void solve() {
	cin >> n >> s;
	int A=0,B=0,C=0,D=0;
	for(int i=0;i<n*4;++i) {
		if(s[i] == 'A') A++;
		else if(s[i] == 'B') B++;
		else if(s[i] == 'C') C++;
		else if(s[i] == 'D') D++;
	}
	A = min(n,A);
	B = min(n,B);
	C = min(n,C);
	D = min(n,D);
	cout << A+B+C+D << "\n"; 
} 

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

B. Array Divisibility

翻译

如果一个整数数组 \(a_1,a_2,\cdots,a_n\)$$ 满足以下条件,那么这个数组就是美丽的整数 \(k\)

  • 所有 \(j\)\(a_{j}\) 之和,其中 \(j\)\(k\) 的倍数,而 $1 \le j \le n $ 是 \(k\) 的倍数。本身是 \(k\) 的倍数。
  • 更正式地说,如果 \(\sum_{k | j} a_{j}\) 能被所有 \(1 \le j \le n\)\(k\) 整除,那么数组 \(a\)\(k\) 的约束下是美丽的。这里,符号 \({k|j}\) 表示 \(k\) 除以 \(j\) ,即 \(j\)\(k\) 的倍数。

给定 \(n\) ,求一个正非零整数数组,其中每个元素都小于或等于 \(10^5\) ,且所有 \(1 \le k \le n\) 都是美丽的。

可以证明答案总是存在的。

思路

题目大意:对于每一个 \(1 \le k \le n\),都要满足 k 以及 k 的倍数的位置上的数字都要能够整除 \(k\)

把题目抽象出来,应该就能写了。如果还是不知道,可以举例子:令 \(n = 4\),当 \(k = 1\) 时,\(a[1],a[2],a[3],a[4]\) 都得是 1 的倍数;当 \(k = 2\) 时,\(a[2],a[4]\) 都得是 2 的倍数;当 \(k = 3\) 时,\(a[3]\) 都得是 3 的倍数;当 \(k = 4\) 时,\(a[4]\) 都得是 4 的倍数。一个数最小的整数倍数是自己,因此,对于每一个 \(k\),我们令 \(a_k = k\),发现正好符合题目要求!所以循环 1~n ,输出每一个数即可。

代码

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;

const int N = 2e5 + 10;

const int inf = 1e9 + 7;

int n,m,k,ans;

void solve() {
	cin >> n;
	for(int i=1;i<=n;++i) cout << i << " ";
	cout << "\n";
} 

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

C. Different String

翻译

给你一个由小写英文字母组成的字符串 \(s\)

\(s\) 中的字符重新排列,组成一个与 \(s\) 不等的新字符串 \(r\) ,或者报告不可能。

思路

一个比较容易想到的想法是,我们每次选两个不一样的字母,交换他们的位置即可。

但是这样做有个问题,我怎么知道我要交换的两个字母在哪里呢?这里提供两个思路:

  • 每次从第二个字符向后扫描,发现与第一个字符不一样的字符,停止扫描并交换这两个字符的位置即可。如果没有这样的字符,则没办法组成新字符串;

  • 直接对这个字符串排序,判断第一个字符和最后一个字符一定不一样,一样则没办法组成新字符串,不一样就可以。

代码

  • 思路 1
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;

const int N = 2e5 + 10;

const int inf = 1e9 + 7;

int n,m,k,ans;

int a[N];

void solve() {
	string s; cin >> s;
	for(int i=1;i<s.size();++i) {
		if(s[i] != s[0]) {
			swap(s[i],s[0]);
			cout << "YES\n" << s << "\n";
			return ;
		}
	}
	cout << "NO\n";
} 

signed main() {
	ios::sync_with_stdio(0);
	//solve();
	int t; cin >> t; while(t--) solve();
	return 0;
}
  • 思路 2
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;

const int N = 2e5 + 10;

const int inf = 1e9 + 7;

int n,m,k,ans;

int a[N];

void solve() {
	string s; cin >> s;
	string last = s;
	sort(s.begin(),s.end());
	if(s != last) {
		cout << "YES\n" << s << "\n";
		return ;
	}
	swap(s[0],s[s.size()-1]);
	if(s == last) cout << "NO\n";
	else {
		cout << "YES\n" << s << "\n";
	}
} 

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

D. Card Exchange

翻译

你有一手 \(n\) 张牌,每张牌上都写着一个数字,还有一个固定整数 \(k\) 。你可以多次进行下面的运算:

  • 从手中的 \(k\) 张牌中任意选择一张数字相同的牌。
  • 将这些牌换成 \(k-1\) 张牌,每张牌上都可以有你选择的数字(包括你刚换的牌上写的数字)。

在这个过程结束时,你手中最少有多少张牌?

思路

我们这样来思考:如果我手上恰好有 \(k\) 张相同数字的牌,我把它置换,最后可以换到 \(k-1\)任意牌,这个时候我的手上如果还有其他的牌,我直接把这 \(k-1\) 张任意牌变成它,就又有 \(k\) 张牌了!如此反复,最终我会剩下 \(k-1\) 张牌(不能再置换了);如果我没有 \(k\) 张相同数字的牌,没有办法置换牌,游戏就结束了。这样一思考,答案就出来了:如果当前手上相同类型的手牌有大于等于 \(k\) 张的,答案一定是 \(k-1\),否则答案为 \(n\)

代码

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;

const int N = 2e5 + 10;

const int inf = 1e9 + 7;

int n,m,k,ans;

int a[N],cnt[N];

void solve() {
	for(int i=1;i<=100;++i) cnt[i] = 0;
	cin >> n >> k;
	int ok = 0;
	for(int i=1,x;i<=n;++i) {
		cin >> x;
		cnt[x] ++;
		if(cnt[x] >= k) ok = 1;
	}
	if(ok) cout << k-1 << "\n";
	else cout << n << "\n";
} 

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

E - 明明的随机数

翻译

中文题目,就不需要翻译了吧:)

思路

听到一些人在讨论随机数,其实读完题目之后就知道其实和随机数一点关系没有。

题目大意就是让我们对对一个数组排序并且去掉重复的元素。观察数组内元素的大小,小于 1000,可以使用桶排序:每次读取数字,使用桶(一个数组)记录这个数字是存在的。最后扫描一遍这个桶(数组),如果某元素存在,输出这个元素,不存在则跳过,接着扫描。

如果你对 STL 有所了解,那么有一个函数 unique() 就可以帮助我们完成去重操作。具体内容可以自己上网查询,这里不再展开。

代码

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;

const int N = 2e5 + 10;

const int inf = 1e9 + 7;

int n,m,k,ans;

int a[N],cnt[N];

void solve() {
	cin >> n;
	for(int i=1,x;i<=n;++i) {
		cin >> x; cnt[x]++;
	} 
	for(int i=1;i<=1000;++i) {
		if(cnt[i] >= 1) ans++;
	}
	cout << ans << "\n";
	for(int i=1;i<=1000;++i) {
		if(cnt[i] >= 1) cout << i << " ";
	}
} 


signed main() {
#ifndef ONLINE_JUDGE
	freopen("q.in","r",stdin);
	freopen("q.out","w",stdout);
#endif		
	ios::sync_with_stdio(0);
	solve();
	//int t; cin >> t; while(t--) solve();
	return 0;
}

送一份使用 unique() 的代码

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;
typedef pair<int,char> pic;

const int N = 2e5 + 10;

const int inf = 1e9 + 7;

int n,m,k;

int a[N];

void solve() {
	cin >> n;
	for(int i=1;i<=n;++i) cin >> a[i];
	sort(a+1,a+1+n);
	int len = unique(a+1,a+1+n) - (a+1);
	cout << len << "\n";
	for(int i=1;i<=len;++i) {
		cout << a[i] << " ";
	}
} 

signed main() {
	ios::sync_with_stdio(0);
	solve();
	return 0;
}

F. Test of Love

翻译

恩科尔愿意为朱伦做任何事,甚至愿意游过鳄鱼出没的沼泽。我们决定测试一下这份爱。恩科尔必须游过一条宽 \(1\) 米、长 \(n\) 米的河流。

河水非常冷。因此,(即从 \(0\)\(n+1\) 的整个游泳过程)恩科尔在水中游泳的总长度不超过 \(k\) 米。为了人性化起见,我们不仅在河里加入了鳄鱼,还加入了可以让他跳上去的圆木。我们的测试如下

一开始,恩科尔在左岸,需要到达右岸。它们分别位于 \(0\)\(n+1\) 米处。河道可以表示为 \(n\) 个河段,每个河段的长度为 \(1\) 米。每个河段都包含原木 "L"、鳄鱼 "C "或水 "W"。恩科的移动方式如下

  • 如果他在水面上(即岸上或圆木上),他可以向前跳跃不超过 \(m\) ( \(1 \le m \le 10\) ) 米(他可以在岸上、圆木上或水中跳跃)。
  • 如果他在水中,他只能游到下一个河段(如果他在 \(n\)\(n\) )米处,则只能游到岸边)。
  • 无论如何,ErnKor 都不能在有鳄鱼的河段登陆。

确定恩科尔能否到达右岸。

思路

读完题目,我们可以发现:恩科尔的游泳距离是有限制的;水里面还有鳄鱼,不可以跨越。因此,我们肯定是能不游泳就不游泳。那么,我们就来模拟一下恩科尔的测试过程:使用变量 L,表示当前的位置,从 L 开始向后枚举,看看 \([L,L+m]\) 这个区间内有没有浮木,有我们就直接跳跃到浮木上面(将 L 更新为这个浮木的位置),否则我们只能跳入水中向前游(将 L 更新为能跳跃的最远距离)。在水中游的时候,碰到浮木就上岸,从这个浮木开始接着向下跳。碰到鲨鱼就结束游戏。

注意累计在水中游的距离以及更新 L 的位置即可,具体实现看代码。

代码

#include<bits/stdc++.h>
#define re register
#define int long long
#define ull unsigned long long
using namespace std;
const int N = 1e6 + 10;
const int inf = 1e9 + 7;

inline int read() {
	int s=0,w=1; char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') w=-1; ch=getchar();}
	while(ch>='0'&&ch<='9') {s=s*10+ch-'0';ch=getchar();}
	return s*w;
}

int n,m,k,ans;

int a[N];

void solve() {
	cin >> n >> m >> k;
	string s; cin >> s;
	for(int i=1;i<=n;++i) {
		if(s[i-1]=='L') a[i] = 1;
		if(s[i-1]=='W') a[i] = 2;
		if(s[i-1]=='C') a[i] = 3;
	}
	a[n+1] = a[0] = 1;  //开始和终点都是陆地哦
	int l = 0,water = 0;
	while(l<=n) {
		if(a[l]==1) { //是陆地
			int ok = 0;
			for(int i=l+1;i<=l+m;++i) {  //向后枚举,看看有没有陆地
				if(a[i]==1) {
					l = i; ok = 1; break; // 有陆地,跳到上面
				}
			}
			if(ok) continue;  //如果有浮木,重新向后枚举,
			if(a[l+m]==2) {l += m; continue;} //否则就跳到水中
			cout << "NO\n"; return ; //如果最远的地方是鳄鱼,游戏结束(游泳过去也会被拦住的)
		}
		if(a[l]==2) {
			int ok = 0;
			for(int i=l+1;water<=k;++i) {
				water++;  // 累计游泳距离
				if(a[i]==3||water>k) {	cout << "NO\n"; return ;} // 如果碰到鳄鱼或者游泳距离大于 k,游戏结束。
				if(i>=n+1) {cout << "YES\n"; return ;}  // 到达终点,游戏胜利
				if(a[i]==1) {l = i; ok = 1; break;} //到达陆地,更新位置
			}
			if(ok) continue;  //这两句其实没啥作用,因为所有的状况都被判断完了(码风不好导致的)
			cout << "NO\n"; return ;
		}
	}
	cout << "YES\n";
}

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

G. Card Game

翻译

两名玩家正在玩一款在线纸牌游戏。游戏使用一副 32 张牌。每张牌都有花色和等级。共有四种花色:梅花、方块、红心和黑桃。我们将分别用字符 "C"、"D"、"H "和 "S "对它们进行编码。共有 8 个等级,依次递增:'2', '3', '4', '5', '6', '7', '8', '9'.

每张牌都用两个字母表示:等级和花色。例如,红心 8 表示为 8H。

游戏开始时,选择一种花色作为王牌花色

在每一轮游戏中,玩家都要这样出牌:第一位玩家将自己的一张牌放在桌上,第二位玩家必须用自己的一张牌击败这张牌。之后,两张牌都被移至弃牌堆。

如果两张牌的花色相同,且第一张牌的等级高于第二张牌,那么这张牌就能打败另一张牌。例如,8S 可以打败 4S。例如,如果王牌花色是梅花("C"),那么 3C 可以击败 9D。请注意,王牌只能被等级更高的王牌击败。

游戏一共进行了 \(n\) 个回合,因此弃牌堆中现在有 \(2n\) 张牌。您想重建游戏中的回合数,但是弃牌堆中的牌是洗过的。请找出游戏中可能出现的 \(n\) 轮次序列。

思路

这题评分在 1k4,主要的难点在于实现。

读完题目后,我们可以了解牌的大小比较:先看他们是王牌还是普通牌,如果是同类牌,再看他们的数字大小。这就是我们的排序方式。由于弃牌堆已经乱序,我们只需要找出一种符合这个排序方法的出牌方法即可。

由于牌类只有王牌与普通牌之分,我们需要对牌按照他们的字母分类,再对每类牌的数字进行排序。同时,使用一个变量记录王牌是哪一类。我们从普通牌开始,每次选择同类的两张牌"输出"即可(已经按照数字排序,可以保证第一张牌始终小于第二张牌,注意不要重复输出相同的牌)。如果某一类牌最后只剩一张了,我们就拿王牌来凑成一对(因为王牌大于普通牌)。最后再看王牌剩余的牌数是否合法即可。如果凑不出来或者王牌剩余数量不合法,那么就是 IMPOSSIBLE.

这个思路很简单,就是排序 + 模拟,不过实现起来略困难,具体可以参考我的代码。

ps:不要急着输出,应该先记录答案,因为在记录过程中可能会有 IMPOSSIBLE 的情况。

代码

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;
typedef pair<int,char> pic;

const int N = 2e5 + 10;

const int inf = 1e9 + 7;

int n,m,king;

char num,ch;

char change(int x) {
	if(x == 1) return 'C';
	if(x == 2) return 'D';
	if(x == 3) return 'H';
	if(x == 4) return 'S';
}

void solve() {
	vector<int> a[5];  // 牌的分类
	vector<pic> ans; // 记录答案
	cin >> n >> ch;  
	if(ch == 'C') king = 1; //记录 王牌 是哪一类
	if(ch == 'D') king = 2;
	if(ch == 'H') king = 3;
	if(ch == 'S') king = 4;
	for(int i=1;i<=n+n;++i) {
		cin >> num >> ch;
		if(ch == 'C') a[1].push_back(num-'0'); //分类,插入数字。
		if(ch == 'D') a[2].push_back(num-'0');
		if(ch == 'H') a[3].push_back(num-'0');
		if(ch == 'S') a[4].push_back(num-'0');
	} 
	for(int i=1;i<=4;++i) sort(a[i].begin(),a[i].end()); // 对每一类内部进行排序
	int cnt = 0; // 记录王牌用到哪了
	for(int i=1;i<=4;++i) {
		if(i == king) continue;  // 王牌要放在最后检查,先判断王牌可能会导致后面没办法凑牌
		for(int j=0;j<a[i].size();j+=2) {
			if(j+1 < a[i].size()) {  // 如果剩余牌大于等于两张
				ans.push_back({a[i][j],change(i)});  // 插入到答案序列中
				ans.push_back({a[i][j+1],change(i)});
			}
			else {  // 只剩一张牌了
				if(cnt < a[king].size()) {  // 王牌还有,拿来凑一对
					ans.push_back({a[i][j],change(i)});
					ans.push_back({a[king][cnt++],change(king)});
				}
				else { // 否则不合法
					cout << "IMPOSSIBLE\n"; return ;
				}
			}
		}
	}
	for(int i=cnt;i<a[king].size();i+=2) {  // 检查王牌剩余数量
		if(i+1<a[king].size()) {  // 如果剩余牌大于等于两张
			ans.push_back({a[king][i],change(king)});
			ans.push_back({a[king][i+1],change(king)});
		}
		else { // 否则不合法
			cout << "IMPOSSIBLE\n"; return ;
		}
	}
	for(int i=0;i<ans.size();i+=2) {  // 输出答案
		cout << ans[i].first << ans[i].second << " " << ans[i+1].first << ans[i+1].second << "\n";
	}
} 

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

完结撒花~

posted @ 2024-10-20 02:12  猫猫不会摸鱼  阅读(63)  评论(0)    收藏  举报