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

A - Takahashi san 2

翻译

KEYENCE 的文化是,无论角色、年龄或职位如何,都用后缀"-san "称呼每个人。

给你一个由小写英文字母组成的字符串 \(S\)
如果 \(S\) 以 "san "结尾,则打印 "是";否则打印 "否"。

思路

按照题目模拟就可以了。

代码

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


int n,m,k;

void solve() {
	string s; cin >> s;
	int len = s.size();
	if(s[len-3] == 's' && s[len-2] == 'a' && s[len-1] == 'n') cout << "Yes\n";
	else cout << "No\n";
	return ;
}

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

B - Unvarnished Report

翻译

KEYENCE 的文化是如实报告事情的好坏。
因此,我们想检查报告的内容是否与原文完全一致。

给你两个字符串 \(S\)\(T\) ,由小写英文字母组成。
如果 \(S\)\(T\) 相等,则打印 \(0\) ;否则,打印它们不同的第一个字符的位置。
在这里,如果 \(i\) /th字符只存在于 \(S\)\(T\) 中的一个,则认为 \(i\) /th字符是不同的。

更确切地说,如果 \(S\)\(T\) 不相等,打印满足以下条件之一的最小整数 \(i\)

  • \(1\leq i\leq |S|\)\(1\leq i\leq |T|\) 和 $ i\neq T_i$ 。
  • \(|S| \lt i \leq |T|\) .
  • \(|T| \lt i \leq |S|\) .

这里, \(|S|\)\(|T|\) 分别表示 \(S\)\(T\) 的长度, \(S_i\)\(T_i\) 分别表示 \(S\)\(T\)\(i\) 个字符。

思路

也没有什么思路,按照题目模拟就可以了。

需要注意的点是:

  • 如果你用的是string,它的下标从 \(0\) 开始,所以输出的时候答案要 \(+1\)

  • \(S\)\(T\) 相等要输出 0

代码

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

const int N = 1e6 + 10;

int n,m,k;

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

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

C - Seats

翻译

一排有 \(N\) 个座位,编号为 \(1, 2, \ldots, N\)

座位的状态由长度为 \(N\) 的字符串 \(S\) 给出,该字符串由 "#"和". "组成。如果 \(S\) 的第 \(i\) 个字符是 "#",则表示座位 \(i\) 有人;如果是".",则表示座位 \(i\) 无人。

求在 \(1\)\(N - 2\) 之间,满足以下条件的整数 \(i\) 的个数:

  • 座位 \(i\)\(i + 2\) 有人,座位 \(i + 1\) 无人。

思路

在 2 ~ n-1 范围内暴力枚举即可。(如果下标从 0 开始的是在 1 ~ n-2 枚举)

代码

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

const int N = 1e6 + 10;

int n,m,k,ans;

string s;

void solve() {
	cin >> n >> s; ans = 0;
	for(int i=1;i<s.size() - 1;++i) {
		if(s[i] == '.' && s[i-1] == '#' && s[i+1] == '#') ans++;
	}
	cout << ans << "\n";
	return ;
}

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


D - Traveling Takahashi Problem

翻译

给你 \(n\) 个点,从 \((0,0)\) 开始按顺序直线走到其他点,最后再回到起点,求走过的路程。

思路

结合翻译和原题面给出的距离公式(欧几里得距离公式),就可以出思路了:设置两个变量 \(x,y\) 表示当前坐标,初始化为 \((0,0)\),每读入一个新坐标,就用公式计算两点的距离,最后更新坐标位置即可。

注意最后还需要回到原点,所以读取完新坐标后还需要额外计算当前坐标 \((x,y)\)\((0,0)\) 的距离即可。

注意要保留小数

代码

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

const int N = 1e6 + 10;

int n,m,k;

double p(double x) {  // 计算平方的,pow写起来太难看
	return x*x;
}

double add(double x,double y,double xx,double yy) { // 计算两点之间距离
	return sqrt(p(xx-x) + p(yy-y));
}

void solve() {
	cin >> n;
	double ans = 0,x = 0,y = 0; // 初始化
	for(int i=1;i<=n;++i) {
		double xx,yy;
		cin >> xx >> yy;
		ans += add(x,y,xx,yy);
		x = xx; y = yy;  // 要记得更新坐标
	}
	ans += add(x,y,0,0); //要回到原点,所以还要算与原点的距离
	cout << fixed << setprecision(10) << ans;
	return ;
}

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

E - Candy Button

翻译

有一个神秘的按钮。当你按下这个按钮时,你会得到一颗糖果,除非距离你上次得到糖果的时间少于 \(C\) 秒。

高桥决定按这个按钮 \(N\) 次。他将在 \(T_i\) 秒后按下 \(1\) 次这个按钮。

他会得到多少颗糖果?

思路

这题关键的数据是距离上次得到糖果的时间,那我们就用一个变量 las 记录上次拿到糖的时间,再和当前按下按钮的时间比较,如果大于等于 \(C\) 秒,那么就更新拿到糖的时间,同时累计答案。

注意它有可能会在第 \(0\) 秒按下按钮,因此我们要将(记录上次拿到糖的时间的变量) las 初始化为 \(-1\)

代码

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

const int N = 1e6 + 10;

int n,m,k,c;

int ans;

void solve() {
	cin >> n >> c;
	int las = -1;  //初始化
	for(int i=1,x;i<=n;++i) {
		cin >> x;
		if(las == -1 || x-las >= c) {  //如果是一开始(没有拿到糖,或者距离上次拿到糖的时间大于 C 秒)
			ans++; las = x;  //统计答案,更新拿糖时间
		}
	}
	cout << ans << "\n";
	return ;
}

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

F - Hands on Ring (Easy)

翻译

给一些指令,每次指令都会选定左手或右手,你需要将被选定的这只手移动到指定位置,但是移动过程中不能碰到另一只手(移动路径上不能存在另一只手),问你操作的最小次数。

思路

一句话总结:讨论左右手和指定位置的相对关系即可。

很容易想到的是,手的移动方向只有两个,还被另外一只手卡住了一个。那就只能向另一边走了。哪个方向被卡住了呢?就需要讨论左右手和指定位置的关系

代码

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

const int N = 1e6 + 10;

int n,m,k,c;

int ans;

void solve() {
	cin >> n >> m;
	int l = 1,r = 2;
	for(int i=1,x;i<=m;++i) {
		char c; cin >> c >> x;
		if(c == 'L') {
			if(r > x) {
				if(l <= x || l < r) ans += abs(x-l);
				else ans += x + n - l;
			}
			else if(r < x) {
				if(l < r) ans += l + n - x;
				else ans += abs(x - l);
			}
			l = x;
		}
		if(c == 'R') {
			if(l > x) {
				if(r <= x || r < l) ans += abs(x-r);
				else ans += x + n - r;
			}
			else if(l < x) {
				if(r < l) ans += r + n - x;
				else ans += abs(x - r);
			}
			r = x;
		}
	}
	cout << ans;
	return ;
}

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

G - Separated Lunch

翻译

由于 KEYENCE 总部的员工越来越多,他们决定将总部各部门分成两组,错开午休时间。

KEYENCE 总部有 \(N\) 个部门, \(i\) /-部门 \((1\leq i\leq N)\) 的人数为 \(K_i\)

将每个部门分配到 \(A\) 组或 \(B\) 组,让每个组在同一时间午休,并确保 \(A\) 组和 \(B\) 组的午休时间不重叠,求同一时间午休的最大人数的最小值。
换句话说,求分配给 \(A\) 组的部门总人数和分配给 \(B\) 组的部门总人数中较大者的最小值。

思路

我们将题目抽象出来:将数分为两个集合,集合的价值为他们所拥有的数之和,问这么多种分配方法中,两个集合较大着的最小值应该是多少。

因为数必须分为两个集合,所以一个数不是 \(A\) 集合就是 \(B\) 集合,那么我们只要知道 \(A\) 集合的价值,就知道了 \(B\) 集合的价值。我们很容易想到的方法是穷举法:穷举选 \(1\) 个人的最大价值最小是多少、穷举选 \(2\) 个人的最大价值最小是多少...,穷举选 \(n/2\) 个人的最大价值最小是多少。为什么到 \(n/2\) 就停止了呢?假设 \(n = 20\),我们选了 \(11\) 个人,那么另一个集合就选了 \(9\) 个,但是选 \(9\) 个这种情况我们已经枚举过了,所以就没必要再枚举了。

观察一下数据范围,\(n \le 20\),最坏时间复杂度是 \(O(2^{20})\)(粗略估计,实际比这个要小),可以使用穷举法。

穷举的方法有两种:\(dfs\) 和 状态压缩。由于猫猫太懒,下面先给出 dfs 做法,状态压缩的做法之后会更新。

代码

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

const int N = 1e6 + 10;

const int inf = 1e9 + 7;

int n,m,k,ans,sum;

int a[N],vis[N];

void dfs(int u,int tans,int lim) {  // 当前搜索到的位置,当前所选的数之和,限制选几个。
	if(lim == 0 || u > n) {
		int num1 = tans;
		int num2 = sum - tans;
		ans = min(ans,max(num1,num2));
		return ;
	} 
	for(int i=u+1;i<=n;++i) {
		if(!vis[i]) {
			vis[i] = 1;
			dfs(i,tans+a[i],lim-1);
			vis[i] = 0;
		}
	}
}

void solve() {
	cin >> n; ans = inf;
	for(int i=1;i<=n;++i) {
		cin >> a[i]; sum += a[i]; // 记得算所有数的总和,否则不知道另一集合的大小
	}
	sort(a+1,a+1+n);  //也可以不排序,不影响。
	for(int i=1;i<=n;++i) { //枚举限制选的个数,枚举到 n 和 枚举到 (n+1)/2 都没有影响。
		dfs(0,0,i);  //每次限制选的个数,再开始搜索
	}
	cout << ans << "\n";
	return ;
}

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

H - Spiral Rotation

翻译

给你一个有 \(N\) 行和 \(N\) 列的网格,其中 \(N\) 是偶数。让 \((i, j)\) 表示从上往下数第 \(i\) 行和从左往上数第 \(j\) 列的单元格。

每个单元格都涂成黑色或白色。如果 \(A_{i, j} =\) 则单元格 \((i, j)\) 为黑色;如果是 \(A_{i, j} =\) .,则为黑色。则为白色。

按以下顺序对 \(i = 1, 2, \ldots, \frac{N}{2}\) 进行操作后,找出每个单元格的颜色。

  • 对于 \(i\)\(N + 1 - i\) 之间的所有整数对 \(x, y\) ,用单元格 \((x, y)\) 的颜色替换单元格 \((y, N + 1 - x)\) 的颜色。同时对所有这些单元格对 \(x, y\) 进行替换

思路

猫猫说不会写,其实是乱说的

之后这里会更新带图片的解释,如果看不懂文字,可以稍微等一会。

我们观察和研究一下替换的范围:所有坐标数字 \((x,y)\) 大小满足 \(i\) ~ \(n-i+1\) 的,都要替换。有点难理解?我们可以稍微模拟一下。

现在我有一个长度为 \(8\) 的矩阵。

  • 起初,\(i = 1\),变化范围为 \(1\)\(n - 1 + 1 = n = 8\),也就是红色区域。

  • 然后,\(i = 2\),变化范围为 \(2\)\(n - 2 + 1 = n - 1 = 7\),也就是绿色区域

  • 接着,\(i = 3\),变化范围为 \(3\)\(n - 3 + 1 = n - 2 = 6\),也就是蓝色区域

  • 最后,\(i = 4\),变化范围为 \(4\)\(n - 4 + 1 = n - 3 = 5\),也就是黄色区域

看完这里,相信你已经了解变化的范围了,接下来看替换规则:替换规则为 \((x,y)\) -> \((y,n-x+1)\)。在坐标轴上画一画,我们就可以发现这个的几何含义是顺时针旋转 90 度。

但是这题数据大小是 \(n \le 3000\),我们是没办法暴力修改每一个块的。我们再研究一下上面的图。可以发现,其实每个“圈”的旋转角度是固定的,例如,红圈每次只转 90 度,绿圈只转 180 度,篮圈只转 270 度,黄圈只转 360 度(相当于没转),再往下呢,又是一个新的循环。所以,每一圈赚多少我们是明确的。第一大外圈要转 90 度,第二大外圈要转 180 度,第三大外圈要转 270 度,第四大外圈不转...,以此类推。做法就出来了。

这题的另外一大难点是坐标的变换,这个需要自己去在草稿纸上模拟,也可以结合代码理解一下。

代码

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

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,sum;

char a[3010][3010],b[3010][3010];

void solve() {
	cin >> n;
	for(int i=1;i<=n;++i) {
		for(int j=1;j<=n;++j) {
			cin >> a[i][j];
		}
	}
	int dir = 0;  // 记录应该怎么转
	for(int i=1;i<=n/2;i++) {
		if(dir == 0) {  // 顺时针旋转 90 度
			for(int j=i;j<=n-i+1;++j) {  //由于是同时修改,所以要先把修改后的状态放在另一个数组,才能实现同时修改
				b[n-i+1][n-j+1] = a[j][n-i+1];  // right -> down
				b[j][i] = a[n-i+1][j]; // down -> left
				b[i][n-j+1] = a[j][i]; // left -> up
				b[j][n-i+1] = a[i][j]; // up -> left
			}
			for(int j=i;j<=n-i+1;++j) {  // 将状态修改回来。
				a[i][j] = b[i][j];
				a[n-i+1][j] = b[n-i+1][j];
				a[j][i] = b[j][i];
				a[j][n-i+1] = b[j][n-i+1];
			}
		}
		if(dir == 1) {// 顺时针旋转 180 度
			for(int j=i;j<=n-i+1;++j) {
				b[n-i+1][n-j+1] = a[i][j]; // up -> down
				b[i][n-j+1] = a[n-i+1][j]; //down -> up
				b[n-j+1][i] = a[j][n-i+1]; //right - > left
				b[n-j+1][n-i+1] = a[j][i]; //left -> right
			}
			for(int j=i;j<=n-i+1;++j) {
				a[i][j] = b[i][j];
				a[n-i+1][j] = b[n-i+1][j];
				a[j][i] = b[j][i];
				a[j][n-i+1] = b[j][n-i+1];
			}
		}
		if(dir == 2) { // 顺时针旋转 270 度
		//这里猫猫偷懒了,执行的是 旋转 90 度 + 旋转 180 度,虽然慢一点,但是也能达到相同的效果。
			for(int j=i;j<=n-i+1;++j) {
				b[n-i+1][n-j+1] = a[j][n-i+1]; 
				b[j][i] = a[n-i+1][j];
				b[i][n-j+1] = a[j][i]; 
				b[j][n-i+1] = a[i][j]; 
			}
			for(int j=i;j<=n-i+1;++j) {
				a[i][j] = b[i][j];
				a[n-i+1][j] = b[n-i+1][j];
				a[j][i] = b[j][i];
				a[j][n-i+1] = b[j][n-i+1];
			}
			for(int j=i;j<=n-i+1;++j) {
				b[n-i+1][n-j+1] = a[i][j];
				b[i][n-j+1] = a[n-i+1][j];
				b[n-j+1][i] = a[j][n-i+1];
				b[n-j+1][n-i+1] = a[j][i];
			}
			for(int j=i;j<=n-i+1;++j) {
				a[i][j] = b[i][j];
				a[n-i+1][j] = b[n-i+1][j];
				a[j][i] = b[j][i];
				a[j][n-i+1] = b[j][n-i+1];
			}
		}
		if(dir == 3) {  // 旋转 360 度,等于没转。
			
		}
		dir = (dir + 1) % 4;  // 重置旋转状态。
	}
	for(int i=1;i<=n;++i) {
		for(int j=1;j<=n;++j) {
			cout << a[i][j];
		}		
		cout << "\n";
	}
	return ;
}

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

I - Prepare Another Box

翻译

\(N\) 个玩具,编号从 \(1\)\(N\) ;有 \(N-1\) 个盒子,编号从 \(1\)\(N-1\) 。玩具 \(i\\ (1 \leq i \leq N)\) 的大小为 \(A_i\) ,盒子 \(i\\ (1 \leq i \leq N-1)\) 的大小为 \(B_i\)

高桥想把所有玩具分别存放在不同的盒子里,他决定按以下步骤依次进行:

  1. 选择一个任意正整数 \(x\) 并购买一个大小为 \(x\) 的盒子。
  2. 将每个 \(N\) 玩具放入 \(N\) 盒中(现有的 \(N-1\) 盒加上新买的盒子)。在这里,每个玩具只能放在一个大小不小于该玩具大小的盒子里,而且任何盒子都不能装两个或两个以上的玩具。

他想通过在步骤 \(1\) 中购买一个足够大的盒子来执行步骤 \(2\) ,但是大盒子的价格更高,所以他想购买尽可能小的盒子。

请判断是否存在一个值 \(x\) 使他可以执行步骤 \(2\) ,如果存在,求最小值 \(x\)

思路

形式化题意:加一个盒子使所有的玩具都能装下,或者说明不可能。

这题猫猫可能做复杂了,下面只讲猫猫自己的思路。当然如果你有更好的思路可以和猫猫说。

为了尽可能不让盒子浪费(大盒子装小玩具),每次我们都选择一个比玩具稍微大一点的盒子来装玩具,这个可以使用 multiset + 二分实现。同时我们需要使新加入的盒子大小最小,那么我们就对玩具从大到小排序,从最大的玩具开始选盒子(如果最大的玩具找不到盒子,那么比它小的玩具也找不到盒子)。如果出现选不到盒子的情况,我们就记录下这个玩具的大小,并跳过这个玩具。

无解的情况是:有两个及以上的玩具,在给定的盒子找不到能装的。由于我们只能添加一个盒子,所以所有玩具都装进盒子中是不可能实现的。

这里说明使用 multiset 的三点原因:盒子大小可能有重复;被选择过的箱子需要删除;箱子序列要保持有序才能二分。

代码

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

const int N = 1e6 + 10;

const int inf = 1e9 + 7;

int n,m,k,ans,sum;

int a[N];

multiset<int> s;

void solve() {
	cin >> n;
	for(int i=1;i<=n;++i) cin >> a[i];
	for(int i=1,x;i<n;++i) cin >> x,s.insert(x);
	sort(a+1,a+1+n);
	for(int i=n;i>=1;--i) {
		set<int>::iterator it = s.lower_bound(a[i]);
		if(*it >= a[i]) {  //能找到盒子
			s.erase(s.lower_bound(a[i])); continue;
		}
		else {// 找不到盒子
			if(ans == 0) {  // 在此之前没有玩具装不下 
				ans = a[i]; 
			}
			else { // 在此之前已经有玩具装不下,只添加一个盒子是不可能的
				cout << -1 << "\n"; return ;  // 注意我直接 return 了,会直接回到主函数。
			}
		}
	}
	cout << ans << "\n";
	return ;
}

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

posted @ 2024-10-27 13:42  猫猫不会摸鱼  阅读(59)  评论(0)    收藏  举报