2月14日打卡

一、阵列

题目:

明明在上学的时候,参加数学兴趣班。在班上,老师介绍了一种非常有趣的阵列。

该阵列由n个正整数构成,阵列中的数字从1开始递增,数字的排序规则是从1开始由中间逆时针向外转出,2出现在1的下面,然后直至输出n为止。

例如当n=5的时候,阵列如下:

5

1 4

2 3

当n=9时,阵列如下:

7 6 5

8 1 4

9 2 3

当n=10时,阵列如下:

7 6 5

8 1 4

9 2 3

10

明明回家后想自己动手构造这样的阵列。他从n=1开始构造,但是他发现当n越来越大时,阵列的复杂性就越高,然后构造出来的阵列就越容易出错。为了降低构造阵列的出错率,提高构造速度,明明就求助于你,请你帮他写一个程序,来构造这样的阵列。

明明的问题可以归结为:给你一个正整数n,请你按题目描述中所述的方法,构造出阵列。

输入说明:你写的程序要求从标准输入设备中读入测试数据作为你所写程序的输入数据。标准输入设备中有多组测试数据,每组测试数据仅占一行,每行仅有一个正整数n(1≤n≤99),即所要构造的阵列的大小。每组测试数据与其后一组测试数据之间没有任何空行,第一组测试数据前面以及最后一组测试数据后面也都没有任何空行。

输出说明:对于每一组测试数据,你写的程序要求计算出一组相应的运算结果,并将这一组运算结果作为你所写程序的输出数据依次写入到标准输出设备中。每组运算结果为一个大小为n的阵列,阵列中的数字用一个空格隔开,具体形式请参考输出样例: 当n为个位数时,输出的每个数占1位,当n为两位数时,两位数所在的列输出的每个数占2位(不足2位的左边补空格)。 每组运算结果与其后一组运算结果之间有一个空行,最后一组运算结果之后没有任何空行。 注:通常,显示屏为标准输出设备。

个人总结:

这道题本质上是一个二维数组模拟 + 螺旋构造(逆时针)问题,核心思想是用"坐标移动"来模拟数字从中心向外逆时针扩展的过程。

从算法角度来看,本题使用的是模拟算法(Simulation)。题目给出的规则是:从数字1开始,位于中心位置,然后按"逆时针"方向向外扩展,顺序为"下 → 右 → 上 → 左",不断绕圈,直到填满1到n为止。这种问题的关键不在数学公式,而在于精确地按照规则一步一步移动坐标,因此最合适的方法就是方向数组 + 步长控制来模拟整个移动过程。

代码中定义了二维数组a[25][25]作为画布,并人为把中心设置在(12,12),这样可以避免数组越界问题。这是一种常见技巧------给足够大的数组空间,让螺旋在中间生长,而不是从(0,0)开始。这一处理是本题的一个隐含技巧,也是容易出错的地方。如果数组不够大或者中心没选好,很容易越界。

在移动控制上,本题的真正难点在于"步长规律"。逆时针螺旋的移动规律是:第一圈移动步数为1、1、2、2;下一圈为3、3、4、4;再下一圈为5、5、6、6......可以归纳为:每绕一圈,步长增加2;在同一圈内,前两个方向走step步,后两个方向走step+1步。代码中通过变量step控制,并在每轮结束后step += 2,实现了这一规律。这一部分是本题算法的核心,也是最容易写错的地方。只要步长控制错一点,整个阵列就会错位。

代码:

#include <stdio.h>

#include <string.h>

int main() {

int n;

int first = 1;

while (scanf("%d", &n) == 1) {

if (!first) printf("\n");

first = 0;

int a[25][25];

memset(a, 0, sizeof(a));

int cx = 12, cy = 12;

int x = cx, y = cy;

a[x][y] = 1;

// 逆时针:下 右 上 左

int dx[4] = {1, 0, -1, 0};

int dy[4] = {0, 1, 0, -1};

int num = 2, step = 1;

while (num <= n) {

for (int d = 0; d < 4 && num <= n; d++) {

int cnt = (d < 2) ? step : step + 1;

for (int k = 0; k < cnt && num <= n; k++) {

x += dx[d];

y += dy[d];

a[x][y] = num++;

}

}

step += 2;

}

// 边界

int minx = 25, maxx = 0, miny = 25, maxy = 0;

for (int i = 0; i < 25; i++) {

for (int j = 0; j < 25; j++) {

if (a[i][j]) {

if (i < minx) minx = i;

if (i > maxx) maxx = i;

if (j < miny) miny = j;

if (j > maxy) maxy = j;

}

}

}

int width = (n >= 10) ? 2 : 1;

// 输出

for (int i = minx; i <= maxx; i++) {

int last = -1;

for (int j = miny; j <= maxy; j++)

if (a[i][j]) last = j;

if (last == -1) continue;

for (int j = miny; j <= last; j++) {

if (j > miny) putchar(' '); // 列间空格(唯一来源)

if (a[i][j] == 0) {

for (int k = 0; k < width; k++) putchar(' ');

} else {

if (width == 2 && a[i][j] < 10) putchar(' ');

printf("%d", a[i][j]);

}

}

putchar('\n');

}

}

return 0;

}

二、饲料调配

题目:

农夫约翰从来只用调配得最好的饲料来为他的奶牛。

饲料用三种原料调配成:大麦,燕麦和小麦。他知道自己的饲料精确的配比,在市场上是买不到这样的饲料的。他只好购买其他三种混合饲料(同样都由三种麦子组成),然后将它们混合,来调配他的完美饲料。

给出三组整数,表示 大麦:燕麦:小麦 的比例,找出用这三种饲料调配 x:y:z 的饲料的方法。

例如,给出目标饲料 3:4:5 和三种饲料的比例:

1:2:3

3:7:1

2:1:2

你必须编程找出使这三种饲料用量最少的方案,要是不能用这三种饲料调配目标饲料,输出'NONE'。'用量最少'意味着三种饲料的用量(整数)的和必须最小。

对于上面的例子,你可以用8份饲料1,2份饲料2,和5份饲料3,来得到7份目标饲料: 8*(1:2:3) + 1*(3:7:1) + 5*(2:1:2) = (21:28:35) = 7*(3:4:5)

以上数字中,表示饲料比例的整数都是小于100(数量级)的非负整数,表示各种饲料的份数的整数都小于100。一种混合物的比例不会由其他混合物的比例直接相加得到。

输入说明:

Line 1: 三个用空格分开的整数,表示目标饲料

Line 2..4: 每行包括三个用空格分开的整数,表示农夫约翰买进的饲料的比例

输出说明:输出文件要包括一行,这一行要么有四个整数,要么是'NONE'。前三个整数表示三种饲料的份数,用这样的配比可以得到目标饲料。第四个整数表示混合前三种饲料后得到的目标饲料的份数。

个人总结:

这道题本质上是一个整数线性组合 + 比例判定 + 暴力枚举搜索的问题。它的核心数学模型可以抽象为:寻找非负整数 x,y,zx, y, zx,y,z,使得

x·F₁ + y·F₂ + z·F₃ = k·T

其中 F₁、F₂、F₃ 是三种买来的饲料比例向量,T 是目标比例向量,k 是某个正整数倍数。也就是说,三种饲料的线性组合必须恰好是目标比例的整数倍。

从算法角度来看,本题采用的是三重循环暴力枚举算法(Brute Force Enumeration)。因为题目明确说明三种饲料用量小于100,所以可以直接枚举 x、y、z 在 0~99 范围内的所有组合,总状态数为 100³ = 1,000,000。这个规模在 C 语言中完全可以接受,因此没有必要使用复杂的线性代数求解或高斯消元等方法。利用数据范围换取算法简洁性,是这道题的一个重要思想。

枚举出某组 (x, y, z) 之后,程序计算混合后的比例向量 B:

B[0] = x·F₁[0] + y·F₂[0] + z·F₃[0]

B[1] = x·F₁[1] + y·F₂[1] + z·F₃[1]

B[2] = x·F₁[2] + y·F₂[2] + z·F₃[2]

接下来判断 B 是否是目标 T 的整数倍,也就是是否存在同一个整数 k,使得:

B[i] = k · T[i] (对所有 i 成立)

这一步是本题的关键判定逻辑,也是最容易出错的地方。

代码:

#include <stdio.h>

int main() {

int T[3], F[3][3];

int x, y, z;

int best_x = -1, best_y = -1, best_z = -1, best_k = -1;

int min_sum = 1000;

// 输入

scanf("%d %d %d", &T[0], &T[1], &T[2]);

for (int i = 0; i < 3; i++) {

scanf("%d %d %d", &F[i][0], &F[i][1], &F[i][2]);

}

for (x = 0; x < 100; x++) {

for (y = 0; y < 100; y++) {

for (z = 0; z < 100; z++) {

int B[3];

B[0] = x*F[0][0] + y*F[1][0] + z*F[2][0];

B[1] = x*F[0][1] + y*F[1][1] + z*F[2][1];

B[2] = x*F[0][2] + y*F[1][2] + z*F[2][2];

int k_values[3];

int k_count = 0;

int possible = 1;

for (int i = 0; i < 3; i++) {

if (T[i] == 0) {

if (B[i] != 0) {

possible = 0;

break;

}

} else {

if (B[i] % T[i] != 0) {

possible = 0;

break;

}

k_values[k_count++] = B[i] / T[i];

}

}

if (!possible) continue;

if (k_count == 0) continue; // 全部 T=0,不合法

int k = k_values[0];

if (k <= 0) continue;

for (int i = 1; i < k_count; i++) {

if (k_values[i] != k) {

possible = 0;

break;

}

}

if (!possible) continue;

int sum = x + y + z;

if (sum < min_sum) {

min_sum = sum;

best_x = x;

best_y = y;

best_z = z;

best_k = k;

}

}

}

}

if (best_x == -1)

printf("NONE\n");

else

printf("%d %d %d %d\n", best_x, best_y, best_z, best_k);

return 0;

}

三、求小数位数个数

题目:明明最近在一家软件公司实习,公司分配给他一个任务,要他写一个小程序,这个程序的功能是求出一个浮点数的小数部分的长度。例如程序输入1.1,则输出1,程序输入1.11,则输出2,明明觉得这个非常简单,花了不到5分钟的时间就把程序给写出来了,然后就把程序交给了测试员测试。但是没有想到的是,经过测试员的测试,发现了一大堆的错误,返回的结果很多都是不对的,这个令明明相当的不解,始终想不通自己的程序错在哪里。你是一名经验丰富的程序员,明明把这个问题来求助于你,明明和你说了他的想法,你一听就明白明明错在了哪里,原来明明使用double型来存放浮点数,但是由于double型的精度问题,不可能把所有的小数都精确的保存好,如果小数位数很长,就会出错。你发现了问题。现在请你写出正确的程序。 明明的问题可以归结为:给你一个浮点数,请你求出这个浮点数的小数位数。

输入说明:你写的程序要求从标准输入设备中读入测试数据作为你所写程序的输入数据。标准输入设备中有多组测试数据,每组测试数据仅占一行,每行仅包括一个浮点数数n,n的长度不超过100。每组测试数据与其后一组测试数据之间没有任何空行,第一组测试数据前面以及最后一组测试数据后面也都没有任何空行。

输出说明:对于每一组测试数据,你写的程序要求计算出一组相应的运算结果,并将这一组运算结果作为你所写程序的输出数据依次写入到标准输出设备中。每组运算结果为一个整数,即n的小数部分的位数。每组运算结果单独形成一行数据,其行首和行尾都没有任何空格,每组运算结果与其后一组运算结果之间没有任何空行,第一组运算结果前面以及最后一组运算结果后面也都没有任何空行。 注:通常,显示屏为标准输出设备。

个人总结:

这道题的本质并不是"浮点数计算问题",而是一个字符串处理问题。题目表面上要求求出浮点数的小数位数,但真正的陷阱在于:不能使用 double 类型进行数值存储和计算。因为 double 采用二进制浮点表示,很多十进制小数无法精确表示,例如 0.1 在二进制中是无限循环的,一旦位数较多就会发生精度丢失。如果用 double 读入再去处理,小数尾部可能被自动截断或变形,从而导致统计位数错误。

因此本题的核心算法思想是:将输入当作字符串处理,而不是数值处理。只要把整行数据作为字符数组读入,然后统计小数点后面的字符个数即可,完全不需要进行任何浮点运算。

代码中使用 char s[1000] 存储输入,通过 strlen 计算字符串长度,再遍历字符串查找小数点 '.' 的位置。这个过程的时间复杂度为 O(n),其中 n 为字符串长度(最大不超过100),计算量极小。

代码:

#include <stdio.h>

#include <string.h>

int main() {

char s[1000];

while (scanf_s("%s", s) == 1)

{

int len = strlen(s);

int dot_pos = -1;

for (int i = 0; i < len; i++)

{

if (s[i] == '.')

{

dot_pos = i;

break;

}

}

if (dot_pos == -1)

{

printf("0\n");

}

else

{

printf("%d\n", len - dot_pos-1);

}

}

return 0;

}

四、英语翻译

1.To return, then, to the switching capabilities of a moderncomputer:computers in the 1970s were generally able to handleeight switches at a time. That is, they could deal with eightbinary digits, or bits, of data, at every cycle. A group of eightbits is called a byte, each byte containing 256 possible patternsof ONs and OFFs (or 1s and Os). Each pattern is the equivalent ofan instruction, a part of an instruction, or a particular type ofdatum, such as a number or a character or a graphics symbol.The pattern 11010010, for example, might be binary data-in this case, the decimal number 210-or it might be an instructiontelling the computer to compare data stored in its switches todata stored in a certain memory-chip location.

回到现代计算机的开关处理能力上来说:20世纪70年代的计算机通常一次能够处理8个开关。也就是说,在每一个运算周期内,它们可以处理8个二进制数字(即8位,bit)的数据。8位二进制数称为一个字节(byte),每个字节包含256种可能的开与关(或1和0)组合模式。每一种模式都可以表示一条指令、一条指令的一部分,或某种特定类型的数据,例如一个数字、一个字符或一个图形符号。例如,模式11010010可以表示二进制数据------在这种情况下,它表示十进制数210------也可以是一条指令,指示计算机将其开关中存储的数据与某个特定存储芯片位置中的数据进行比较。

2.The development of processors that can handle 16, 32, and 64bits of data at a time has increased the speed of computers. Thecomplete collection of recognizable patterns-the total list of operations-of which a computer is capable is called itsinstruction set. Both factors-the number of bits that can behandled at one time, and the size of instruction sets-continueto increase with the ongoing development of modern digitalcomputers.

能够一次处理16位、32位和64位数据的处理器的发展,提高了计算机的运行速度。计算机所能识别的全部模式------也就是它能够执行的全部操作的总列表------被称为其指令集。随着现代数字计算机的不断发展,这两个方面------一次能够处理的位数以及指令集的规模------都在持续增长。

3.Moderndigital computersallconceptually similar,areregardless of size. Nevertheless, they can be divided into severalcategories on the basis of cost and performance: the personalcomputer or microcomputer, a relatively low-cost machine,usually of desktop size (though "laptops" are small enough to fit in a briefcase, and "palmtops" can fit into a pocket); theworkstation, a microcomputer with enhanced graphics andcommunications capabilities that make it especially useful foroffice work; the minicomputer, generally too expensive forpersonal use, with capabilities suited to a business, school, orlaboratory; and the mainframe computer, a large, expensivemachine with the capability of serving the needs of majorbusiness enterprises, government departments, scientificresearch establishments, or the like (the largest and fastest ofthese are called super computers).

现代数字计算机在概念上都是相似的,无论其规模大小如何。不过,根据成本和性能的不同,它们可以分为几类:个人计算机或微型计算机,这是一种成本相对较低、通常为台式大小的机器(不过"笔记本电脑"小到可以放进公文包,"掌上电脑"甚至可以放入口袋);工作站,是一种具有增强图形和通信能力的微型计算机,特别适用于办公工作;小型计算机,通常价格较高,不适合个人使用,其功能适用于企业、学校或实验室;以及大型主机计算机,这是一种体积庞大、价格昂贵的机器,能够满足大型企业、政府部门、科研机构等的需求(其中最大、最快的被称为超级计算机)。

五、单词打卡

相关推荐
blackicexs2 小时前
第四周第七天
算法
期末考复习中,蓝桥杯都没时间学了3 小时前
力扣刷题19
算法·leetcode·职场和发展
Renhao-Wan3 小时前
Java 算法实践(四):链表核心题型
java·数据结构·算法·链表
zmzb01034 小时前
C++课后习题训练记录Day105
开发语言·c++·算法
好学且牛逼的马4 小时前
【Hot100|25-LeetCode 142. 环形链表 II - 完整解法详解】
算法·leetcode·链表
H Corey5 小时前
数据结构与算法:高效编程的核心
java·开发语言·数据结构·算法
SmartBrain5 小时前
Python 特性(第一部分):知识点讲解(含示例)
开发语言·人工智能·python·算法
01二进制代码漫游日记5 小时前
自定义类型:联合和枚举(一)
c语言·开发语言·学习·算法
小学卷王6 小时前
复试day25
算法