喊7是一个传统的聚会游戏,N个人围成一圈,按顺时针从1到N编号。
编号为1的人从1开始喊数,下一个人喊的数字为上一个人的数字加1,但是当将要喊出来的数字是7的倍数或者数字本身含有7的话,不能把这个数字直接喊出来,而是要喊”过”。
假定玩这个游戏的N个人都没有失误地在正确的时机喊了”过”,当喊到数字K时,可以统计每个人喊”过”的次数。
现给定一个长度为N的数组,存储了打乱顺序的每个人喊”过“的次数,请把它还原成正确的顺序,即数组的第i个元素存储编号i的人喊”过“的次数。
输入为一行,为空格分隔的喊”过“的次数,注意K并不提供,K不超过200,而数字的个数即为N。
输出为一行,为顺序正确的喊”过“的次数,也由空格分隔。
输入
0 1 0
输出
1 0 0
说明
一共只有一次喊”过“,那只会发生在需要喊7时,按顺序,编号为1的人会遇到7,故输出1 0 0。
注意
结束时的K不一定是7,也可以是8、9等,喊过的次数都是1 0 0。
输入
0 0 0 2 1
输出
0 2 0 1 0
说明
一共有三次喊”过“,发生在7 14 17,按顺序,编号为2的人会遇到7 17,编号为4的人会遇到14,故输出0 2 0 1 0。
本题是约瑟夫环问题,有多种解法,
初始化变量:
N
:输入的数组长度,即参与游戏的人数。skip_counts
:输入的喊”过“的次数数组。result
:长度为N的数组,用于存储每个人喊”过“的次数,初始化为0。计算每个人喊”过“的次数:
person_index = (数字 - 1) % N
。result[person_index]
的值。还原顺序:
result
数组中的值与skip_counts
数组中的值进行匹配,确保result
数组中的值与输入的喊”过“的次数一致。输出结果:
result
数组中的每个元素,用空格分隔。通过这种方法,我们可以有效地还原每个人喊”过“的次数。时间复杂度为O(K),其中K是喊到的最大数字,因为我们只需要遍历从1到K的所有数字。
以下是 Java 代码的详细中文注释与讲解。此代码的功能是模拟一个游戏,游戏规则如下:
游戏规则:
n
个人围成一圈,依次报数。7
的倍数,或者包含数字 7
,则喊“过”。输入格式:
arr
,表示每个人需要喊“过”的次数目标。输出格式:
import java.util.Arrays;
import java.util.Scanner;
import java.util.StringJoiner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 读取输入的一行,按空格分割并转换为整数数组
Integer[] arr =
Arrays.stream(sc.nextLine().split(" ")).map(Integer::parseInt).toArray(Integer[]::new);
// 调用算法并输出结果
System.out.println(getResult(arr));
}
public static String getResult(Integer[] arr) {
// totalGo表示一共需要喊多少次“过”
int totalGo = Arrays.stream(arr).reduce(Integer::sum).orElse(0);
// n表示一共几个人
int n = arr.length;
// p[i]表示每个人实际喊“过”的次数,初始为0
int[] p = new int[n];
// i表示当前报的数字,j表示当前轮到哪个人
int i = 1;
int j = 0;
while (totalGo > 0) {
// 如果当前数字是7的倍数,或者包含数字7,则需要喊“过”
if (i % 7 == 0 || (i + "").contains("7")) {
totalGo--; // 总喊“过”次数减1
p[j]++; // 当前人喊“过”次数加1
}
i++; // 报数加1
j++; // 轮到下一个人
// 如果j超过人数范围,则回到第一个人
if (j >= n) j = 0;
}
// 使用StringJoiner将结果拼接为字符串,用空格分隔
StringJoiner sj = new StringJoiner(" ");
for (int v : p) {
sj.add(v + "");
}
return sj.toString();
}
}
Scanner sc = new Scanner(System.in);
// 读取输入的一行,按空格分割并转换为整数数组
Integer[] arr =
Arrays.stream(sc.nextLine().split(" ")).map(Integer::parseInt).toArray(Integer[]::new);
Scanner
类:
sc.nextLine()
读取一行输入。Arrays.stream()
:
map(Integer::parseInt)
将字符串转换为整数。toArray(Integer[]::new)
将流转换为整数数组。public static String getResult(Integer[] arr) {
// totalGo表示一共需要喊多少次“过”
int totalGo = Arrays.stream(arr).reduce(Integer::sum).orElse(0);
// n表示一共几个人
int n = arr.length;
// p[i]表示每个人实际喊“过”的次数,初始为0
int[] p = new int[n];
// i表示当前报的数字,j表示当前轮到哪个人
int i = 1;
int j = 0;
while (totalGo > 0) {
// 如果当前数字是7的倍数,或者包含数字7,则需要喊“过”
if (i % 7 == 0 || (i + "").contains("7")) {
totalGo--; // 总喊“过”次数减1
p[j]++; // 当前人喊“过”次数加1
}
i++; // 报数加1
j++; // 轮到下一个人
// 如果j超过人数范围,则回到第一个人
if (j >= n) j = 0;
}
// 使用StringJoiner将结果拼接为字符串,用空格分隔
StringJoiner sj = new StringJoiner(" ");
for (int v : p) {
sj.add(v + "");
}
return sj.toString();
}
变量说明:
totalGo
:总需要喊“过”的次数,初始为数组 arr
中所有元素的和。n
:人数,即数组 arr
的长度。p
:数组,表示每个人实际喊“过”的次数。i
:当前报的数字,从 1
开始递增。j
:当前轮到哪个人,范围是 0
到 n-1
。逻辑说明:
totalGo
。p
,用于记录每个人实际喊“过”的次数。while
循环模拟报数过程:
7
的倍数,或者包含数字 7
,则减少 totalGo
,并增加当前人的喊“过”次数。1
,轮到下一个人。StringJoiner
将结果拼接为字符串,用空格分隔。// 使用StringJoiner将结果拼接为字符串,用空格分隔
StringJoiner sj = new StringJoiner(" ");
for (int v : p) {
sj.add(v + "");
}
return sj.toString();
StringJoiner
类:
sj.add(v + "")
将整数转换为字符串并添加到结果中。输入:
1 1 1
输出:
1 1 1
解释:
3
。7
、14
、17
等会触发喊“过”。输入:
2 2 2
输出:
2 2 2
解释:
6
。7
、14
、17
、21
、27
、28
等会触发喊“过”。功能:
优点:
StringJoiner
简化字符串拼接。适用场景:
如果您有其他问题,欢迎随时提问!
以下是 Python 代码的详细中文注释与讲解。此代码的功能是模拟一个游戏,游戏规则如下:
游戏规则:
n
个人围成一圈,依次报数。7
的倍数,或者包含数字 7
,则喊“过”。输入格式:
arr
,表示每个人需要喊“过”的次数目标。输出格式:
# 输入获取
arr = list(map(int, input().split()))
# 算法入口
def getResult():
# totalGo表示一共需要喊多少次“过”
totalGo = sum(arr)
# n表示一共几个人
n = len(arr)
# p[i]表示每个人实际喊“过”的次数,初始为0
p = [0] * n
# i表示当前报的数字,j表示当前轮到哪个人
i = 1
j = 0
while totalGo > 0:
# 如果当前数字是7的倍数,或者包含数字7,则需要喊“过”
if i % 7 == 0 or str(i).find("7") != -1:
totalGo -= 1 # 总喊“过”次数减1
p[j] += 1 # 当前人喊“过”次数加1
i += 1 # 报数加1
j += 1 # 轮到下一个人
# 如果j超过人数范围,则回到第一个人
if j >= n:
j = 0
# 将结果数组拼接为字符串,用空格分隔
return " ".join(map(str, p))
# 算法调用
print(getResult())
# 输入获取
arr = list(map(int, input().split()))
input().split()
:
map(int, ...)
:
list(...)
:
map
对象转换为列表。# totalGo表示一共需要喊多少次“过”
totalGo = sum(arr)
# n表示一共几个人
n = len(arr)
# p[i]表示每个人实际喊“过”的次数,初始为0
p = [0] * n
# i表示当前报的数字,j表示当前轮到哪个人
i = 1
j = 0
while totalGo > 0:
# 如果当前数字是7的倍数,或者包含数字7,则需要喊“过”
if i % 7 == 0 or str(i).find("7") != -1:
totalGo -= 1 # 总喊“过”次数减1
p[j] += 1 # 当前人喊“过”次数加1
i += 1 # 报数加1
j += 1 # 轮到下一个人
# 如果j超过人数范围,则回到第一个人
if j >= n:
j = 0
变量说明:
totalGo
:总需要喊“过”的次数,初始为数组 arr
中所有元素的和。n
:人数,即数组 arr
的长度。p
:列表,表示每个人实际喊“过”的次数,初始为 0
。i
:当前报的数字,从 1
开始递增。j
:当前轮到哪个人,范围是 0
到 n-1
。逻辑说明:
totalGo
。p
,用于记录每个人实际喊“过”的次数。while
循环模拟报数过程:
7
的倍数,或者包含数字 7
,则减少 totalGo
,并增加当前人的喊“过”次数。1
,轮到下一个人。# 将结果数组拼接为字符串,用空格分隔
return " ".join(map(str, p))
map(str, p)
:
p
中的整数转换为字符串。" ".join(...)
:
输入:
1 1 1
输出:
1 1 1
解释:
3
。7
、14
、17
等会触发喊“过”。输入:
2 2 2
输出:
2 2 2
解释:
6
。7
、14
、17
、21
、27
、28
等会触发喊“过”。功能:
优点:
sum
和 join
简化计算和输出。适用场景:
如果您有其他问题,欢迎随时提问!
以下是 C++ 和 C 语言的版本,并附带详细的中文注释和讲解。
#include
#include
#include
#include
using namespace std;
// 算法入口
string getResult(vector<int>& arr) {
// totalGo表示一共需要喊多少次“过”
int totalGo = 0;
for (int num : arr) {
totalGo += num;
}
// n表示一共几个人
int n = arr.size();
// p[i]表示每个人实际喊“过”的次数,初始为0
vector<int> p(n, 0);
// i表示当前报的数字,j表示当前轮到哪个人
int i = 1;
int j = 0;
while (totalGo > 0) {
// 如果当前数字是7的倍数,或者包含数字7,则需要喊“过”
if (i % 7 == 0 || to_string(i).find('7') != string::npos) {
totalGo--; // 总喊“过”次数减1
p[j]++; // 当前人喊“过”次数加1
}
i++; // 报数加1
j++; // 轮到下一个人
// 如果j超过人数范围,则回到第一个人
if (j >= n) {
j = 0;
}
}
// 将结果数组拼接为字符串,用空格分隔
string result;
for (int k = 0; k < n; k++) {
result += to_string(p[k]);
if (k != n - 1) {
result += " ";
}
}
return result;
}
int main() {
// 输入获取
string input;
getline(cin, input);
stringstream ss(input);
vector<int> arr;
int num;
while (ss >> num) {
arr.push_back(num);
}
// 算法调用
cout << getResult(arr) << endl;
return 0;
}
输入获取:
getline(cin, input)
读取一行输入。stringstream
将输入按空格分割并转换为整数数组。算法逻辑:
totalGo
。p
,用于记录每个人实际喊“过”的次数。while
循环模拟报数过程:
7
的倍数,或者包含数字 7
,则减少 totalGo
,并增加当前人的喊“过”次数。1
,轮到下一个人。输出结果:
string
拼接结果数组,用空格分隔。#include
#include
#include
// 算法入口
char* getResult(int* arr, int n) {
// totalGo表示一共需要喊多少次“过”
int totalGo = 0;
for (int i = 0; i < n; i++) {
totalGo += arr[i];
}
// p[i]表示每个人实际喊“过”的次数,初始为0
int* p = (int*)calloc(n, sizeof(int));
// i表示当前报的数字,j表示当前轮到哪个人
int i = 1;
int j = 0;
while (totalGo > 0) {
// 如果当前数字是7的倍数,或者包含数字7,则需要喊“过”
if (i % 7 == 0 || strchr("7", i + '0') != NULL) {
totalGo--; // 总喊“过”次数减1
p[j]++; // 当前人喊“过”次数加1
}
i++; // 报数加1
j++; // 轮到下一个人
// 如果j超过人数范围,则回到第一个人
if (j >= n) {
j = 0;
}
}
// 将结果数组拼接为字符串,用空格分隔
char* result = (char*)malloc(1000 * sizeof(char));
int pos = 0;
for (int k = 0; k < n; k++) {
pos += sprintf(result + pos, "%d", p[k]);
if (k != n - 1) {
pos += sprintf(result + pos, " ");
}
}
free(p);
return result;
}
int main() {
// 输入获取
char input[1000];
fgets(input, 1000, stdin);
int arr[1000];
int n = 0;
char* token = strtok(input, " ");
while (token != NULL) {
arr[n++] = atoi(token);
token = strtok(NULL, " ");
}
// 算法调用
char* result = getResult(arr, n);
printf("%s\n", result);
free(result);
return 0;
}
输入获取:
fgets
读取一行输入。strtok
将输入按空格分割并转换为整数数组。算法逻辑:
totalGo
。p
,用于记录每个人实际喊“过”的次数。while
循环模拟报数过程:
7
的倍数,或者包含数字 7
,则减少 totalGo
,并增加当前人的喊“过”次数。1
,轮到下一个人。输出结果:
sprintf
拼接结果数组,用空格分隔。C++ 和 C 的区别:
vector
和 string
简化数组和字符串操作。malloc
和 free
。适用场景:
如果您有其他问题,欢迎随时提问!
华为OD(Outsourcing Developer,外包开发工程师)是华为针对软件开发工程师岗位的一种招聘形式,主要包括笔试、技术面试以及综合面试等环节。尤其在笔试部分,算法题的机试至关重要。
机试是进入技术面的第一关:
华为OD机试(常被称为机考)主要考察算法和编程能力。只有通过机试,才能进入后续的技术面试环节。
技术面试需要手撕代码:
技术一面和二面通常会涉及现场编写代码或算法题。面试官会注重考察候选人的思路清晰度、代码规范性以及解决问题的能力。因此提前刷题、多练习是通过面试的重要保障。
入职后的可信考试:
入职华为后,还需要通过“可信考试”。可信考试分为三个等级:
2024年8月14日之后,华为OD机试的题库转为 E卷,由往年题库(D卷、A卷、B卷、C卷)和全新题目组成。刷题时可以参考以下策略:
关注历年真题:
适应新题目:
掌握常见算法:
华为OD考试通常涉及以下算法和数据结构:
保持编程规范:
官方参考:
加入刷题社区:
寻找系统性的教程:
刷题的过程可能会比较枯燥,但它能够显著提升编程能力和算法思维。无论是为了通过华为OD的招聘考试,还是为了未来的职业发展,这些积累都会成为重要的财富。
本地编写代码
调整心态,保持冷静
输入输出完整性
快捷键使用
Ctrl+D
,复制、粘贴和撤销分别为 Ctrl+C
,Ctrl+V
,Ctrl+Z
,这些可以正常使用。Ctrl+S
,以免触发浏览器的保存功能。浏览器要求
交卷相关
时间和分数安排
考试环境准备
技术问题处理
祝你考试顺利,取得理想成绩!