2025年3月31日 GGG2
前言
这次二面因为 Virtual Judge
诡异的判题账号绑定和手有余香验证码浪费了一些时间 , 题目多是考察语法掌握能力和模拟 , H
题是二分查找 , I
题是贪心 . 相比于去年的二面题目简单很多 .
A B2110 找第一个只出现一次的字符
A
题明显是一个数组映射 , 遍历记录索引字符出现的次数 , 再重新遍历数组即可 . 我在重新遍历时是按照字典顺序输出的字符而不是按照字符串顺序 , 因此测试用例 8 / 10 , 极其愚蠢 , 时间复杂度 O(n)
.
#include<bits/stdc++.h>
using namespace std;
int arr[26];
int main() {
string s;
cin >> s;
int len = s.length();
for (int i = 0; i < len; i++) {
arr[s[i] - 'a']++;
}
for (int i = 0; i < len; i++) {
if (arr[s[i] - 'a'] == 1) {
char ch = s[i];
cout << ch << endl;
return 0;
}
}
cout << "no" << endl;
return 0;
}
B B2136 素数回文数的个数
B
题的限制条件 素数 和 回文数 可以设计成分别的函数方法单独检测 , 我选择使用欧拉筛生成从 2
到 n
的素数数组 , 遍历数组利用反转字符串和原字符串是否相等判断回文数 , getPrimes
方法时间复杂度 O(n)
, huiwen
方法时间复杂度 O(d == log n)
, d 为 素数位数 , 主函数时间复杂度 O(len * d == n / log n * d == n)
, len
为 从 2
到 n
的素数个数 : 总时间复杂度为 O(n)
.
#include<bits/stdc++.h>
using namespace std;
vector<int> getPrimes(int n){
vector<int> primes;
int top = -1;
bool isPrime[n+1];
memset(isPrime, true, sizeof(isPrime));
isPrime[0] = isPrime[1] = false;
for(int i = 2; i <= n; i++){
if(isPrime[i]){
primes.push_back(i);
++top;
}
for(int j = 0; j <= top && i * primes[j] <= n; j++){
isPrime[i * primes[j]] = false;
if(i % primes[j] == 0){
break;
}
}
}
return primes;
}
bool huiwen(int n){
string s = to_string(n);
string temp = s;
reverse(s.begin(), s.end());
return s == temp;
}
int main(){
int n;
cin >> n;
vector<int> primes = getPrimes(n);
int len = primes.size();
int sum = 0;
for(int i = 0; i < len; ++i){
if(primes[i] >= 11 && huiwen(primes[i])){
++sum;
}
}
cout << sum << endl;
return 0;
}
C B2111 基因相关性
C
题是简单的对位检测 , 遍历标记最后和全长结合判断输出即可 . 时间复杂度 O(n)
.
#include<bits/stdc++.h>
using namespace std;
int main(){
double a;
cin >> a;
string s1;
cin >> s1;
string s2;
cin >> s2;
int len1 = s1.length();
int sum = 0;
for(int i=0;i<len1;i++){
if(s1[i] == s2[i]){
++sum;
}
}
double res = (double)sum/len1;
if(res > a){
cout<<"yes"<<endl;
}
else{
cout<<"no"<<endl;
}
return 0;
}
D P1152 欢乐的跳
D
题求解数组相邻元素差的绝对值集合是否包括 1
到 n - 1
的所有整数 , 创建布尔差数组遍历点亮从 0
到 n - 2
的索引值与后继索引值的差的绝对值索引为 true
, 我在开始想到了掩码反码的位运算可以代替 abs
方法获得绝对值 , 但出于时间紧张直接选择了 abs
方法 . 时间复杂度 O(n)
.
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
int arr[n];
for(int i=0;i<n;i++){
cin >> arr[i];
}
bool a[n];
memset(a,false,sizeof(a));
for(int i = 0; i < n - 1; ++i) {
int temp = abs(arr[i] - arr[i + 1]);
if (temp >= 1 && temp <= n - 1) {
a[temp] = true;
}
}
for(int i = 1; i <= n - 1; ++i){
if(a[i] == false){
cout << "Not jolly" << endl;
return 0;
}
}
cout << "Jolly" << endl;
return 0;
}
E B2092 开关灯
E
题的核心是反转 , 我选择使用单独的 final
方法返回布尔反转值 , 倍数遍历部分使用直观的嵌套循环完成 . 内循环反转操作处理 i 的倍数 , 求和其调和级数为 ln n + γ
, 总时间复杂度 O(n * log n)
.
#include<bits/stdc++.h>
using namespace std;
bool fan(bool a){
return a == true ? false : true;
}
int main(){
int n;
cin>>n;
bool arr[n + 1];
memset(arr, false, sizeof(arr));
for(int i = 2; i <= n; ++i){
for(int j = 1; j <= n; ++j){
if(j % i == 0){
arr[j] = fan(arr[j]);
}
}
}
for(int i = 1; i <= n; ++i){
if(arr[i] == false){
cout<<i<<" ";
}
}
}
F B2140 二进制分类
F
题考察对十进制转换二进制的理解 , 只需要统计二进制数中 1
和 0
的个数 , 因此对 i
反复除 2
直至 i == 0
, 统计每次模处理最后比对即可 . 时间复杂度 O(n * log n)
.
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
int a = 0, b = 0;
for(int i = 1;i <= n;i++){
int one = 0, zero = 0;
int temp = i;
while(temp != 0){
if(temp % 2 == 0) zero++;
else one++;
temp /= 2;
}
if(one > zero) a++;
else b++;
}
cout << a << " " << b << endl;
}
G B3639 T2 点亮灯笼
G
题可以作为 E
题的翻版 , 注意模拟灯笼的头尾是相连接的 , 对应到数组即额外处理边界情况即可 , 时间复杂度 O(m + n)
.
#include<bits/stdc++.h>
using namespace std;
bool fan(bool a){
return a == true ? false : true;
}
int main(){
int n, m;
cin>>n>>m;
bool arr[n];
memset(arr, false, sizeof(arr));
int a;
for(int i = 0; i < m; i++){
cin >> a;
a--;
arr[a] = fan(arr[a]);
if(a > 0)arr[a - 1] = fan(arr[a - 1]);
if (a == 0) arr[n - 1] = fan(arr[n - 1]);
if(a < n - 1)arr[a + 1] = fan(arr[a + 1]);
if (a == n - 1) arr[0] = fan(arr[0]);
/*
for (int i = 0; i < n; i++) {
cout << arr[i] << " ";
}
cout << endl;
*/
}
for(int i = 0; i < n; i++){
cout << arr[i] << " ";
}
}
H P2249 【深基13.例1】查找
H
题提供单调不减的数组和较大数据 , 明示我们使用二分查找来解决 , 问题是首次出现的索引不一定是二分查找落在的位置 , 我使用如果前面和当前索引值相同则前移的笨方法 , 测试用例 0 / 10 … 悲伤 , 我常用开区间的二分查找 , 缺少变通 , 也是二分的题目刷题较少 不熟练 . 时间复杂度 O(m * log n)
.
#include<bits/stdc++.h>
using namespace std;
int find(int arr[],int n,int tar){
int begin = 0;
int end = n - 1;
int res = -1;
while (begin <= end) {
int mid = (end + begin) / 2;
if(arr[mid] < tar){
begin = mid + 1;
}else{
end = mid - 1;
if (arr[mid] == tar) {
res = mid;
}
}
}
return (res != -1) ? res + 1 : -1;
}
int main(){
int n, m;
cin>>n>>m;
int arr[n];
for(int i=0;i<n;i++){
cin>>arr[i];
}
int a[m];
for(int i=0;i<m;i++){
cin>>a[i];
}
for (int i=0;i<m;i++) {
cout << find(arr,n,a[i]) << ' ';
}
}
I P1223 排队接水
I
题是典型显然的贪心题目 , 我们能容易的找到平均等待时间最少的状态即是接水时间长的在后的情况 , 因为这样对后面等待时间的影响最小 , 因此要对原数组进行排序并计算 , 需要输出编号交换后的索引 , 我在这里定义了一个包括索引和接水时间的结构体 , 想要使用 sort
方法和 Lambda
表达式来处理 , 但对 Lambda
表达式不熟练 , 屡次失败 , 还好 C++
允许结构体直接赋值 . 时间复杂度 O(n ^ 2)
.
#include<bits/stdc++.h>
using namespace std;
struct node{
int val;
int index;
};
void swap(node &a,node &b){
node temp=a;
a=b;
b=temp;
}
void sort(node arr[],int n){
for(int i=0;i<n-1;i++){
for(int j=0;j<n-i-1;j++){
if(arr[j].val>arr[j+1].val){
swap(arr[j],arr[j+1]);
}
}
}
}
int main(){
int n;
cin >> n;
node arr[n];
for(int i=0;i<n;i++){
cin >> arr[i].val;
arr[i].index = i + 1;
}
sort(arr,n);
double a[n];
a[0] = 0;
double sum = 0.0f;
for(int i=0;i<n;i++) {
cout << arr[i].index << ' ';
}
cout << endl;
for(int i = 1; i < n; i++){
a[i] = a[i - 1] + arr[i - 1].val;
sum += a[i];
}
sum /= n;
printf("%.2lf\n",sum);
return 0;
}