分类 编程 下的文章

项目地址

DHEnry-FMCRAFT/websocket-chat: 来自AI编写的websocket版的聊天程序

💬 WebSocket 聊天室应用

一个基于 WebSocket 的实时聊天室应用,支持多频道聊天、管理员功能和用户管理。

🚀 功能特性

👥 用户功能

  • 🆓 自动用户名分配 - 首次进入自动生成5位随机用户名
  • 🔄 多频道切换 - 支持在多个频道间自由切换
  • 👀 用户列表查看 - 查看指定频道的在线用户
  • 💬 实时消息收发 - 基于 WebSocket 的实时通信
  • 🎨 彩色消息显示 - 不同类型消息使用不同颜色区分

⚡ 管理员功能

  • 🔐 管理员认证 - 使用密码验证管理员身份
  • 👮 用户管理 - 踢出用户、断开用户连接
  • 📢 频道管理 - 清退频道、关闭频道
  • 👁️ 全服监控 - 查看全服在线用户
  • 📩 私信功能 - 向指定用户发送私信

🛠️ 技术栈

  • 后端: Python + asyncio + websockets
  • 前端: 纯 Python 命令行客户端
  • 通信: WebSocket + JSON
  • 安全: SHA-256 密码哈希

📦 安装与运行

1. 克隆项目

git clone https://github.com/DHEnry-FMCRAFT/websocket-chat
cd websocket-chat

2. 安装依赖

pip install websockets

3. 配置服务器的允许频道

请编辑server.py中的ALLOWED_CHANNELS数组,逗号分隔,引号包裹允许的频道,注意允许的频道一定要包含public

4. 配置服务器管理员密码HASH

请编辑hash.py中的test_string变量,为初始密码,然后使用以下代码运行

python hash.py

然后将生成后的hash设置为server.py中的ADMIN_PASSWORD_HASH变量的值,然后保存server.py

5. 启动服务器

python server.py

服务器默认监听 0.0.0.0:8765,允许的频道:public、1、2、3

6. 启动客户端

python client.py

🎯 使用指南

基本命令

命令功能示例
::login [用户名]设置用户名::login Alice
::choose [频道ID]切换频道::choose 2
::list [频道ID]查看频道用户::list 1
exit / quit退出聊天exit

管理员命令

命令功能示例
::lists查看全服用户::lists
::say [频道] [用户] [消息]发送私信::say 1 Bob "Hello"
::kicks [频道] [用户] [理由]踢出用户::kicks 1 Bob "违规发言"
::kick [频道] [理由]清退频道::kick 1 "频道维护"
::closes [频道] [用户] [理由]断开用户::closes 1 Bob "多次违规"
::close [频道] [理由]关闭频道::close 1 "封禁频道"

🔒 管理员登录

要使用管理员功能,请使用用户名 administrator 登录,系统会提示输入密码。

⚙️ 配置说明

当前默认EXAMPLE服务器配置

  • 默认端口: 8765
  • 允许频道: public, 1, 2, 3
  • 管理员密码哈希: 在 server.py 中配置

客户端配置

  • 调试模式: 修改 DEBUG 变量切换默认服务器地址,1为使用localhost:8765,用于本地调试server.py,0为需要用户自行输入服务器地址
  • 消息颜色: 不同类型消息使用不同颜色标识

🐛 故障排除

常见问题

  1. 连接被拒绝 - 确保服务器已启动且地址正确
  2. 用户名已存在 - 更换用户名或切换到其他频道
  3. 频道不允许 - 只允许使用预定义的频道ID

日志查看

服务器会在控制台输出运行状态和错误信息,客户端会显示连接状态和错误提示。

📝 开发说明

项目结构

websocket-chat/
├── server.py          # 服务器主程序
├── client.py          # 客户端主程序
└── README.md          # 项目说明

来源

这个项目其实是 $Deepseek \times Doubao$ 一起写出来的(乱用LaTex),也是有很多的BUG,但也是在我的命令下写出来的

这个项目最终会放到 $FMCRAFT$

历程

2025年10月1日 PR V1.0 Release V1.0 · DHEnry-FMCRAFT/websocket-chat V1.0

已知BUG

  • 登录时直接进入频道会导致无法发送消息,需要先自定义一个用户名

感谢AI

关于double和float精度丢失的问题

起因

这个问题,我记得我是在五年级的时候就有了,情况大概如下

现在,你需要将π放进一个变量中,需要保留到小数点后100位

过程

完成代码

第一次代码

当时,我想着,不是so easy嘛,然后编写出了如下代码

#include <bits/stdc++.h>
 using namespace std;
 ​
 int main(){
     float x=3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679;
     cout<<x;
 }

然后,我怀着激动的心情,颤抖的手,内心OS:马上就可以AK了!哈哈哈哈!!! ,但是,现实给我了种种一拳

*  正在执行任务: C:\SOFTWARE\Dev-Cpp\MinGW64\bin\g++.EXE -Wall -Wextra -g3 -std=c++23 e:\double.cpp -o e:\output\double.exe 
 PS E:\output> & .\'double.exe'
 3.14159

我:

?

What FUCK???

这感觉不太对啊,这输出怎么能这样子

哦!我忘了,还有double!

使用double完成

然后编写出了如下代码

#include <bits/stdc++.h>
 using namespace std;
 ​
 int main(){
     double x=3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679;
     cout<<x;
 }

一键运行:

*  正在执行任务: C:\SOFTWARE\Dev-Cpp\MinGW64\bin\g++.EXE -Wall -Wextra -g3 -std=c++23 e:\double.cpp -o e:\output\double.exe 
 PS E:\output> & .\'double.exe'
 3.14159

?

没保存吗

Try again

*  正在执行任务: C:\SOFTWARE\Dev-Cpp\MinGW64\bin\g++.EXE -Wall -Wextra -g3 -std=c++23 e:\double.cpp -o e:\output\double.exe 
 PS E:\output> & .\'double.exe'
 3.14159

What FUCK?

怎么还是这样?

那时候我情绪崩溃了,不知道咋回事

然后,我突然想起来了保留小数点的方法!即setprecision

使用setprecision的方法完成

然后写出了如下代码

#include <bits/stdc++.h>
 using namespace std;
 ​
 int main(){
     double x=3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679;
     cout<<setprecision(100)<<x;
 }

嘿嘿嘿,兄弟们,终于可以AK了!!!

*  正在执行任务: C:\SOFTWARE\Dev-Cpp\MinGW64\bin\g++.EXE -Wall -Wextra -g3 -std=c++23 e:\double.cpp -o e:\output\double.exe 
 PS E:\output> & .\'double.exe'
 3.141592653589793115997963468544185161590576171875

az

好像确实精确了亿点,但没有完全精确吧?

怀疑人生中。。。

哦!还有printf!!!

使用printf的方法完成

#include <bits/stdc++.h>
 using namespace std;
 ​
 int main(){
     double x=3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679;
     printf("%.100lf",x);
 }

这下总不能错了,printf可是C语言的,怎么可能错??

可是...

现实又给我了重重一锤

*  正在执行任务: C:\SOFTWARE\Dev-Cpp\MinGW64\bin\g++.EXE -Wall -Wextra -g3 -std=c++23 e:\double.cpp -o e:\output\double.exe 
PS E:\output> & .\'double.exe'
3.1415926535897931159979634685441851615905761718750000000000000000000000000000000000000000000000000000

我是不配学编程吗???

为什么显示总是这么打击我

呜呜呜呜呜呜呜呜

呜呜呜呜呜呜

现在的情况

2024年,Deepseek-R1模型发布

我草,牛逼!

2025年9月24日,一学校老师向我询问此问题,又够引了我的兴趣

  1. IEEE 754 浮点数标准

计算机使用固定的位数来存储浮点数:

  • float(单精度):32位,约6-7位有效数字
  • double(双精度):64位,约15-16位有效数字
  • long double:通常80位或128位
  1. double 的存储结构

double 使用64位存储:

  • 1位符号位
  • 11位指数位
  • 52位尾数位

这意味着 double 只能精确表示大约15-16位十进制数字。

以上内容来自Deepseek-R1

EMM,似懂非懂,但是大概了解了

浮点数在计算机中以二进制分数形式存储,无法精确表示所有十进制小数。

以上内容来自Deepseek-R1

《二进制分数》

我嘞个二进制分数

依旧是补码,对吧???

二进制存储原理

double 的52位尾数只能表示有限精度的分数:

3.141592653588888... = 无法用有限二进制精确表示

计算机找到最接近的可表示二进制分数,这就是你看到的"奇怪"数字。

以上内容来自Deepseek-R1

我的评价:六百六十六

  1. 浮点数都是近似值
  2. 不要用 == 直接比较浮点数
  3. 需要高精度时使用特殊库或整数运算

以上内容来自Deepseek-R1

我的评价:近似值???

现在看来,我为什么不使用先乘再除呢?

#include <bits/stdc++.h>
 using namespace std;
 ​
 int main(){
     unsigned long long x=31415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679;
     cout<<double(x/99);
 }

于是,我就发现了

*  正在执行任务: C:\SOFTWARE\Dev-Cpp\MinGW64\bin\g++.EXE -Wall -Wextra -g3 -std=c++23 e:\double.cpp -o e:\output\double.exe 
 ​
 e:\double.cpp:5:26: warning: integer constant is too large for its type
     5 |     unsigned long long x=31415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679;
       |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 PS E:\output> & .\'double.exe'
 1.07941e+17

哦对对对对对对对,范围啊,我草

奇怪了,为什么我们不使用高精度数组呢?

使用数组

#include <bits/stdc++.h>
 using namespace std;
 ​
 int main(){
     string x="31415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679";
     cout<<x;
 }

运行!

PS E:\output> & .\'double.exe'
 31415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679

耶!成功!

完

致谢

什么是浮点数?

浮点数就是用来表示小数(实数)的数据类型。让我用通俗易懂的方式解释:

什么是浮点数?

浮点数 = 科学计数法的二进制版本

就像我们写很大的数会用科学计数法:

  • 1,200,000 = 1.2 × 10⁶
  • 0.000012 = 1.2 × 10⁻⁵

计算机用类似的方法表示小数,但是使用二进制。

浮点数的组成

浮点数由三部分组成(以32位float为例):

[符号位] [指数部分] [小数部分]
  1位      8位       23位

举例: 数字 -13.625 在计算机中如何表示?

  1. 符号位:负号 → 1
  2. 转换为二进制:

    • 整数部分:13 = 1101(二进制)
    • 小数部分:0.625 = 0.101(二进制)
    • 合并:13.625 = 1101.101(二进制)
  3. 科学计数法:1101.101 = 1.101101 × 2³

    • 小数部分:1.101101 → 去掉前面的1,得到 101101
    • 指数部分:3

为什么叫"浮点数"?

"浮点"的意思是小数点的位置可以浮动:

// 这些数都是浮点数,小数点位置不同
float a = 123.45;     // 小数点在第3位后
float b = 1.2345;     // 小数点在第1位后  
float c = 12345.0;    // 小数点在第5位后

浮点数的实际问题

#include <stdio.h>

int main() {
    // 问题1:有些十进制小数无法精确表示为二进制小数
    float f = 0.1f;  // 0.1在十进制中很简单,但在二进制中是无限循环的!
    
    printf("0.1 的实际存储值: %.20f\n", f);
    
    // 问题2:连续相加会产生误差
    float sum = 0.0f;
    for (int i = 0; i < 10; i++) {
        sum += 0.1f;
    }
    printf("0.1相加10次: %.10f\n", sum);  // 可能不是精确的1.0
    
    return 0;
}

生活中的类比

想象一把尺子:

  • 整数:只能量整厘米(1cm, 2cm, 3cm...)
  • 浮点数:可以量毫米(1.1cm, 1.2cm, 1.25cm...),但精度有限

关键点:

  1. 浮点数不是绝对精确的(就像尺子有最小刻度)
  2. 表示范围很大,可以表示非常小或非常大的数
  3. 运算可能产生微小误差

编程中的浮点数类型

#include <stdio.h>
#include <float.h>

int main() {
    printf("float 精度: 约%d位有效数字\n", FLT_DIG);
    printf("double 精度: 约%d位有效数字\n", DBL_DIG);
    
    printf("float 范围: %e 到 %e\n", FLT_MIN, FLT_MAX);
    printf("double 范围: %e 到 %e\n", DBL_MIN, DBL_MAX);
    
    return 0;
}

简单总结: 浮点数就是计算机用来表示小数的近似方法,虽然不完美,但足够应对大多数科学计算和工程应用的需求。

经典疑问

float 和 double 的区别

特性floatdouble
大小4字节 (32位)8字节 (64位)
精度约6-7位有效数字约15-16位有效数字
范围±3.4e-38 到 ±3.4e+38±1.7e-308 到 ±1.7e+308
关键字floatdouble

printf 中的格式说明符

#include <stdio.h>

int main() {
    float f = 3.141592653589793f;    // 注意后面的 f
    double d = 3.141592653589793;
    
    // 正确的格式说明符
    printf("float: %f\n", f);        // 默认显示6位小数
    printf("double: %lf\n", d);      // %lf 用于 double
    
    // 控制小数位数
    printf("float(2位): %.2f\n", f);
    printf("double(4位): %.4lf\n", d);
    
    // 科学计数法表示
    printf("科学计数法: %e\n", f);
    printf("科学计数法: %le\n", d);
    
    return 0;
}

浮点数的玄学问题

#include <stdio.h>
#include <math.h>

int main() {
    // 问题1:精度损失
    float a = 0.1f;
    float b = 0.2f;
    float c = a + b;
    printf("0.1 + 0.2 = %.10f\n", c);  // 可能不是精确的 0.3
    
    // 问题2:比较浮点数不要直接用 ==
    if (fabs(c - 0.3f) < 1e-6) {      // 使用误差范围比较
        printf("c ≈ 0.3\n");
    }
    
    // 问题3:大数吃小数
    float big = 1e8f;
    float small = 1.0f;
    printf("大数 + 小数 = %f\n", big + small);  // 可能还是 100000000
    
    return 0;
}

四舍五入的正确用法

#include <stdio.h>
#include <math.h>

int main() {
    double numbers[] = {3.4, 3.5, 3.6, -2.3, -2.7};
    int size = sizeof(numbers) / sizeof(numbers[0]);
    
    printf("四舍五入示例:\n");
    for (int i = 0; i < size; i++) {
        printf("%.1f -> %.0f\n", numbers[i], round(numbers[i]));
    }
    
    // 其他相关的舍入函数
    double x = 2.7;
    printf("\n其他舍入函数:\n");
    printf("round(%.1f) = %.0f  (四舍五入)\n", x, round(x));
    printf("ceil(%.1f) = %.0f   (向上取整)\n", x, ceil(x));
    printf("floor(%.1f) = %.0f  (向下取整)\n", x, floor(x));
    printf("trunc(%.1f) = %.0f  (向零取整)\n", x, trunc(x));
    
    return 0;
}

更新日志

V1.0 - 2025年9月17日

正文

Guarding the circle for a moment, clearing away a pack of dogs.

前言

这个文章包含了对hydro的讽刺,但是仅仅是个人观点,仅供参考

1.Hydro对HUSTOJ的强大鄙视

HUSTOJ群精华的方面

贴两张于HUSTOJ用户群的群精华截图:
hydrosb1.png
hydrosb2.png

Hydro的顶级引流

证据找不到了
但是我清清楚楚的记得
有一个人问有没有php的oj,hydro维护者说用狗屎hustoj

hydro.js.org的侧面讽刺

功能HydroHustOJSYZOJ1QDUOJVijos
安装一键脚本一键脚本手动搭建dockerdocker
数据库MongoDBMySQLMariaDBPostgresMongoDB
测试数据存储本地/S32本地本地本地数据库
多评测机+=3=4=+
测试数据同步按需抓取全量同步全量同步全量同步按需抓取
比赛ACM/OI/IOI/乐多ACM/OIACM/OI/IOIACM/OI/IOIACM/OI
封榜++---
作业功能++--+
修改编译命令/添加语言+=5--+
权限系统6+=--+
训练计划(题单)++-7-+
团队+8---+
Hack+----
SpecialJudge+9++-=
Subtask+-+--
交互题+-+--
RemoteJudgeCF/SPOJ/UOJ/POJ/LuoguHDU/PKU---
题目导入fps/syzoj/qduoj/hydrofps/qduojsyzojfps/qduoj-
3. 安装配置繁琐,且需要手动在服务器间同步数据。↩
4. 需要手动在服务器间同步数据。↩
5. 仅能修改部分编译参数,添加语言需要修改源代码。↩
6. 此处的权限系统指 除用户/管理员二元化权限之外的 的更细粒度的权限划分。↩
7. 部分二次开发版本有此功能。↩
8. 通过域功能,允许用户创建域并在域内拥有管理员权限。域之间仅共享账号数据,也可使用域内小组进行权限控制。↩
9. 支持所有主流 SPJ 格式。↩

来源于介绍 https://hydro.js.org/
这里将hydro开发者将hustoj和hydro放在一起,也许是为了对比,剩下的就不说了

2.Hydro的逆天服务

用户管理需要收费

这玩意儿我是真他妈想不通

代安装Hydro

收费50人民币 逆天

《狗叫费》

说话≈狗叫,所以要交狗叫费,逆天Hydro

3.HydroQQ群的逆天交流氛围

咋改logo
看文档

怎么导入hustoj的题目
看文档去

导入不了hustoj的题目
你他妈看文档会死是吗

我的评价:回复一下会死吗

而且回复消息慢的一批

4.Hydro的逆天导入hustoj题目功能

如果你在有hustoj的系统上装hydro
hydro会问你要不要导入hustoj的题目
当你选择是
安装完
你会发现根本没导入
(逆天)

5.Hydro的逆天导入fps

他妈自己写一个格式就nb了是不?fps超过50MB就导入不了?真有你的 (乐)

6.管理不集中化,改一个东西去一个页面

这玩意儿我是真他妈想不通

7.旧版本还能修改测试数据文件保存目录

新版就不行了???
逆天

8.Hydro与洛谷对hustoj的鄙视

具体见洛谷开发文档
hydrosb3.png

9.我的黑历史

我在Hydro群发了一个图片
就被T了
乐
好难猜啊~

我知道这篇文章会招引来很多Hydro的皇家护卫犬,我的评价:那咋了?

最终评价

未若HUSTOJ
感谢https://www.akoj.top/提供了一些相关资料

教你部署hustoj

wget http://dl.hustoj.com/install.sh
bash install.sh

HUSTOJ常见问答

本网站与各大浏览器长期合作,可以按 Ctrl+F搜索关键词

切记

玄学

遇事不慌,先重启服务器试试
因为作者太懒了,有些改动你可能需要重启服务器才能生效
这个文档有点玄学了

学习能力

如果文档里面有看不懂的词汇,可以在官方文档继续查找或者在本文章内继续查找
请学会使用deepseek

安全性

本文章提供的所有指令都需要在root用户的身份下运行

广告

建议阅读关于我对Hydro的感想 - 壹鲦热带鱼的小窝

官方常见问答

代码的那些事 | 程序员回忆录

更常见的常见问答

Q:怎么才能人性化的操作数据库?

建议安装phpmyadmin,具体方法百度一下
oj不能装在phpmyadmin

Q:我怎么查找哪个文件在哪里

find /home/judge -name "FILE_NAME"

在 FILE_NAME处查找你需要的文件名

Q:HUSTOJ可以装在哪里

情况1:web+判题建议装在服务器
情况2:web装虚拟主机 判题装服务器或者远程判题

Q:宝塔用户到底如何安装hustoj

先安装好宝塔+lnmp环境+docker
左侧创建数据库,数据库名和用户名必须是jol,密码随意,然后记住这些信息
在终端运行

wget http://dl.hustoj.com/install-ubuntu22.04-bt.sh
bash install-ubuntu22.04-bt.sh

(仅限于Ubuntu22.04)
然后键入数据库名和密码
回车
期间可能会多次出现需要你手动按下回车的情况
我记得需要按3次
安装完后,宝塔网站创建php站点,域名可以先填ip+端口,网站目录是 /home/judge/src/web
创建完后,编辑站点,左侧网站目录,关闭防跨站攻击
然后左侧应用商店,搜索php,设置,禁用函数里去掉 putenv的禁用,然后扩展里安装 yaml的扩展
宝塔防火墙里放行端口,idc服务商放行端口

Q:网站大部分的配置文件在哪里 and 怎么让学生可以看见错误对比 and 怎么修改备案号 and 怎么修改主题 and 怎么修改css and 如何修改网站标题 and 如何开启论坛功能 and 为什么状态页面看不见管理员的提交 and 我怎么设置不允许注册or注册需要审核 and smtp怎么设置

/include/db_info.inc.php

Q:如何重启判题机

sudo pkill -9 judged && sudo judged

Q:为什么我提交判题卡在编译中

  • 如果修改了数据库的密码,需要修改 db_info.inc.php和 judge.conf两个文件,然后重启判题机
  • judge.conf中如果 OJ_USE_DOCKER=1,则 OJ_SHM_RUN=0
  • run0、run1等文件夹是真实的目录,并非软链
  • 如果 OJ_USE_DOCKER=0可以判题,OJ_USE_DOCKER=1时无法判题,请检查 install目录下 Dockerfile是否符合要求(可以问问Deepseek),然后再次运行 docker.sh看看结果是否正常
  • 修改 OJ_USER_DOCKER参数,需要重启judged生效
  • 刚装的系统,建议重新运行 docker.sh

Q:我怎么取消当前的判题任务

适用于解决卡死的判题任务,新手的话建议先进oj,然后后台管理,左边系统管理-重判任务,人工判题编译中改判其他结果
或者给自己添加HTTP_JUDGER权限,重新登录oj,然后在判题列表中鼠标放到卡死的判题结果上面,下面改成其他结果,记得输入分数

Q:宝塔面板无法导入Hydro的题目咋办

PHP安装yaml扩展

Q:我的oj经常502咋办

从理论上讲,就是你机器配置太拉了,建议升级配置
治标不治本的方法:
重启php和mysql(具体方法deepseek)
也许治本的方法:
宝塔应用商店找到php,设置,配置修改,按照以下的设置进行设置

short_open_tag开启短标签支持
max_execution_time300最大脚本运行时间
max_input_time60最大输入时间
memory_limit 128M脚本内存限制
post_max_size50MPOST数据最大尺寸
file_uploads开启是否允许上传文件
upload_max_filesize50M允许上传文件的最大尺寸
max_fil_uploads20允许同时上传文件的最大数量
fault_socket_timeout60Socket超时时间
error_reportingE_ALL & ~E_NOT错误级别
display_errors开启是否输出详细错误信息
cgi.fix_pathinfo是否开启pathinfo
date.timezonePRC时区

Q:不小心手欠把自己的administrator权限删了咋办 and 不小心把数据库改炸了咋办

sudo su
cd /home/judge/src/install
echo "insert into jol.privilege values('admin','administrator','true','N');" | ./mysql.sh

或者(不推荐 但是理论上可以)

sudo su
cd /home/judge/src/install
./mysql.sh

Q:不小心二开炸了咋办 and 怎么升级oj?

可以执行 install目录下的 fixing.sh
或者重新运行安装脚本(理论可以,但不推荐)

Q:如何修改底部文字?

/template/syzoj/footer.php

Q:自己改过题目id,然后添加题目页面不显示咋办

创建一道id为1000的题目

Q:markdown latex渲染不正确咋办

1 检查自己浏览器
2 更新hustoj的版本
3 请确保使用的是 <span class='md auto_select'></span>而不是 [md][/md] 文本编辑器

Q:底部的菜单栏在哪里修改

/template/syzoj/header.php

Q:我想把结果显示的英文改成类似 AC或 Accpted这种咋办

修改 /lang/cn.php,查找关键字修改

Q:如何批量给题目增加标签?

批量的话,建议直接操作sql
先 mysql -p登录sql服务器,密码就填 db_info.inc.php里头的密码

USE jol;
UPDATE problem 
SET source = CONCAT(source, ' 标签') 
WHERE problem_id BETWEEN 起始编号 AND 结束编号;

注意标签前面要加一个空格

Q:怎么删除公告

数据库中删除

Q:深色模式header和页面太窄了怎么办

在template的header加两个 <br>

Q:我怎么给网站加logo

先将logo放在服务器上,然后编辑template的 header.php,在OJ_NAME前面插入图片,注意调整比例

Q:填空题到底怎么搞

题面exam P110 - 文本题测试 - TropicalFish-OnlineJudge

怎么添加文本题题目?先创建题目,然后最底部改成文本评测

注意题目描述要这么写

<span class='md auto_select'>
题目内容,支持markdown
</span>

例如本题的题目描述是这么写的:

<span class='md auto_select'>
## 你好!
## 这是本OJ的第一道文本题!
## 你可以在题面中点击一个选项,以选择选项!
## 祝你愉快!
1. 以下属于面向对象的高级语言是()。
- C++
- 汇编语言
- C
- Basic
<br>
</span>

另外,如果想要多选,请在当前题号的前面加上多选字样

关于测试数据这么写:

data.in:一行,有N道题,在本题,data.in是 1,注意,题号只能是数字,要有序

data.out:N行,每行是这样构成的:题号 [分数] 正确答案,例如,在本题,data.out是 1 [100] A,不要有多余内容,一行一个

template.c:N行,每行一个题号加一个空格,例如,在本题 template.c是 1 ,注意一题的右边要有一个空格,然后再换行

template.cc:N行,每行一个题号加一个空格,例如,在本题 template.cc是 1 ,注意一题的右边要有一个空格,然后再换行

Q:latex是啥 我不知道啊 and markdown是啥 我不知道啊

可以看看洛谷
LaTeX 格式手册 | 洛谷帮助中心
洛谷 Markdown 格式手册 | 洛谷帮助中心
肥肠好用
或者使用在线LaTeX公式编辑器-编辑器

Q:如何指定输入输出文件

找到题目,编辑测试数据,依次设置下面两个
image.png
image.png

Q:怎么一键导出AC代码(远古功能)

访问 OJ_URL/export_ac_code.php,前提是你登录了

Q:比赛怎么设置oi模式?

oj默认是ioi模式,设置单个比赛oi,可以在添加比赛的禁止方勾选比赛进行中,结束后才能查看结果。

Q:如何快速构造测试数据

可以丢给deepseek,这样写:

你好,请为我写一个随机测试数据生成器,生成xx组测试数据,题目是这样的:
标准程序是这样的:
额外的数据点要求:
请输出python的生成代码

Q:进不去github

下载 watt toolkit

Q:为什么感觉开oj之后磁盘占用过大

最新版oj有自动备份,在 /var/backups下

Q:我怎么迁移

适用于所有方法:
压缩 /home/judge,导出数据库
新服务器先安装hustoj,确保能正常判题,解压judge文件夹覆盖,然后导入数据库(不需要删除)
重置数据库密码

Q:权限表里的记录都是什么含义?

  • “administrator” 管理员,除查看源码外的所有权限。
  • “problem\_editor”:题目编辑者,添加和编辑题目。
  • “source\_browser”:查看审核所有提交的源代码,对比可疑的相似代码。
  • “contest\_creator”:组织创建比赛,编辑比赛。
  • “http\_judge”:HTTP远程判题账号。
  • “password\_setter”:重置普通账户密码。
  • “printer”:现场赛打印员,注意该账户的school字段用于分配打印任务,如果只有一个打印区则置空。
  • “balloon”:气球配送员,注意该账户的school字段用于分配气球任务,如果只有一个气球配送区则置空。
  • “vip”:可以参与所有标题含有VIP字样的私有比赛。
  • “problem\_start”:HTTP远程判题账户的任务起始题号。
  • “problem\_end”:HTTP远程判题账户的任务结束题号。

Q:怎么使用题单

安装好后oj会给exam,仿照即可

Q:怎么卸载oj

install目录下的 uninstall.sh

Q:oj老是超时怎么办

加ios加速头,可以看安装好oj的faq

Q:查重咋搞

OJ_SIM_ENABLE=1

修改/home/judge/src/web/include/db\_info.inc.php,设置

$OJ_SIM=true;
  • 抄袭只对不同账号间生效,自己抄袭自己不计。拥有Source\_browser权限的账号可以看到具体数值和对比。
  • 至少要50个词汇以上,才会判断相似度,过于短小的程序不会进行判断。
  • 长期使用查重功能,需要占用较多磁盘空间和CPU时钟,可以考虑用下面的命令考虑定期清理:

    • cd /home/judge/data
    • find -name ‘ac’ -exec rm -rf {} \\;
    • 清理前注意备份,避免误操作

Q:导入题目失败咋办

1、先用谷歌浏览器直接打开xml文件,看是否有语法错误,如果有,用文本编辑器修订提示的行号。
2、如果超过100M,可以先用EasyFPSViewer拆分成多个小文件,然后再导入。
3、对于HUSTOJ,可以先压缩为zip再上传导入
4、修改/etc/php/7.2/fpm/php.ini, 提高post\_max\_size、upload\_max\_filesize 、memory\_limit、max\_execution\_time 的值。修改后执行sudo service php7.2-fpm restart生效。

Q:比赛添加页面,子网是什么东西

假设有机房a,有学生考试,如果机房a的学生a给机房b的学生b比赛页面,那么机房b的学生b就可以访问比赛,从而帮助他提交代码
但是,设置子网后,机房b的学生就无法进去,包括其他网段

Q:为什么oj主页莫名奇妙出现更新数据库

建议别管他,换个账号登录
执行mysql.sh试试

Q:OJ判题会得部分分怎么办

编辑 /home/judge/etc/judge.conf,OJ_INTERNAL_MARK1改0
然后重启判题机

Q:subtask到底是啥,怎么绑定 and 我怎么做到只要错一个测试点就全错

subtask是啥可以看看洛谷
怎么绑定?

官方:命名规则: .in/.out 对应,[]标记分数,.标记subtask , 如 TaskA.a[20].in/out 和 TaskA.b[20].in/out 是一组subtask,共计40分。

说人话:
{备注名}.subtask名[subtask分].测试数据编号.in/out

例如

这里,problem是题目备注名,a是subtask名,100是subtask分数,1-6是测试点编号,.in/.out是后缀名

Q:离线环境下怎么安装hustoj and 公司内网怎么安装hustoj and 无外网怎么安装hustoj

材料:
一台能用的电脑
找一个不要钱的U盘
安装hustoj的服务器

先去http://123.158.39.181:8090/下载镜像,然后下载rufus,用rufus写入镜像到不要钱的u盘
然后把u盘插入要安装hustoj服务器,百度搜索主板+启动键选择u盘启动,安装即可

Q:宝塔安装的hustoj, 使用hello算法乱码咋办

宝塔面板里面站点配置文件,设置伪静态

location / {
    try_files $uri $uri/ /index.php?$query_string;
}

location ~ \.php$ {
    fastcgi_pass unix:/tmp/php-cgi-82.sock;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
}

将82设置为实际的PHP版本号,中间不要有其他符号

Q:如何重启整个服务

sudo systemctl restart nginx
sudo systemctl restart php-fpm
sudo systemctl restart mysql
sudo pkill -9 judged
sudo judged

试题

上海市2025CSP-J十连测Round 7.pdf

大感

我们这一次并没有批分数,所以关于上海市2025CSP-J十连测Round 7的内容我们会分为$Part 1$和$Part 2$
这一次我估计很难

上海市2025CSP-J十连测Round 7 题干

一、单项选择题

共15题、每题2分,共计30分,每题有且仅有一个正确选项。

  1. 如果开始时计算机处于小写输入状态,现在有一只小老鼠反复按照CapsLock、字母键A、字母键S和字母键D的顺序循环按键,即CapsLock、A、S、D、CapsLock、A、S、D……则屏幕上输出的第2025个字符是字母()。
    A. A
    B. S
    C. D
    D. a
  2. 在8位二进制补码中,10101011表示的是十进制下的()。
    A. -85
    B. -43
    C. 43
    D. 85
  3. 给定一个含N个不相同数字的数组,在最坏情况下,找出其中最大或最小的数,至少需要$N-1$次比较操作。则最坏情况下,在该数组中同时找最大与最小的数至少需要()次比较操作。(「」表示向上取整,⌊⌋表示向下取整)
    A. $\left\lceil\frac{3N}{2}\right\rceil - 2$
    B. $\left\lfloor\frac{3N}{2}\right\rfloor - 2$
    C. $2N - 2$
    D. $2N - 4$
  4. 表达式$a*(b+c)-d$的后缀表达形式为()。
    A. $abc d*+-$
    B. $abc+*d-$
    C. $abc*+d-$
    D. $-+*abcd$
  5. 约定二叉树的根节点高度为1。一棵结点数为2025的二叉树最少有()个叶子结点;一棵结点数为2025的二叉树最小的高度值是()。
    A. 1,11
    B. 1,12
    C. 2,11
    D. 2,12
  6. 向一个栈顶指针为$hs$的链式栈中插入一个指针s指向的结点时,应执行()。
    A. $hs \to next = s;$
    B. $s \to next = hs; hs = s;$
    C. $s \to next = hs \to next; hs \to next = s;$
    D. $s \to next = hs; hs = hs \to next$
  7. 2-3树是满足两个条件的树:①所有叶结点到根的路径长度相同;②所有非叶子结点有两个或三个子结点。如果一棵2-3树有10个叶结点,那么它可能有()个非叶结点。
    A. 3
    B. 4
    C. 6
    D. 8
  8. 由四个没有区别的点构成的简单无向连通图的个数是()。
    A. 6
    B. 7
    C. 8
    D. 9
  9. 设含有10个元素的集合的全部子集数为S,其中由7个元素组成的子集数为T,则$\frac{T}{S}$的值()。
    A. $\frac{5}{32}$
    B. $\frac{15}{128}$
    C. $\frac{1}{8}$
    D. $\frac{21}{125}$
  10. 如下图所示,共有13个格子。对任何一个格子进行一次操作,会使得它自己以及与它上下左右相邻的格子中的数字改变(由1变0,或由0变1)。现在要使得所有的格子中的数字都变为0,至少需要()次操作。
    A. 3
    B. 4
    C. 5
    D. 6
  11. 一个1×8的方格图形(不可旋转)用黑、白两种颜色填涂每个方格。如果每个方格只能填涂一种颜色,且不允许两个黑格相邻,共有()种填涂方案。
    A. 8
    B. 55
    C. 56
    D. 64
  12. 设G是有n个结点、m条边($n \leq m$)的连通图,必须删去G的()条边,才能使得G变成一棵树。
    A. $m - n + 1$
    B. $m - n$
    C. $m + n + 1$
    D. $n - m + 1$
  13. 有7个一模一样的苹果,放到3个一样的盘子中,一共有()种放法。
    A. 7
    B. 8
    C. 21
    D. 2187
  14. 若$f_0 = 0$,$f_1 = 1$,$f_{n+1} = \frac{f_n + f_{n-1}}{2}$,则随着i的增大,$f_i$将接近于()。
    A. $\frac{1}{2}$
    B. $\frac{2}{3}$
    C. $\frac{\sqrt{5} - 1}{2}$
    D. 1
  15. 以下是32位机器和64位机器的区别是()。
    A. 显示器不同
    B. 硬盘大小不同
    C. 寻址空间不同
    D. 输入法不同

二、阅读程序

判断题1分,选择题3分,共计40分;判断题正确填T,错误填F。

第1题

int solve1(int n) {
    n++;
    int size = 0;
    int digit[16];
    int pow = 1;
    int s = 0;
    while (n > 0) {
        digit[size] = n % 10;
        size++;
        n /= 10;
        s += pow;
        pow *= 5;
    }
    while (size > 0) {
        --size;
        pow /= 5;
        s += (digit[size] / 2) * pow;
        if (digit[size] % 2 == 0) break;
    }
    return s - 1;
}

int solve2(int n) {
    int s = 0;
    for (int i = 1; i <= n; i += 2) {
        int t = i;
        bool pass = true;
        while (t > 0) {
            int x = t % 10;
            if (x % 2 == 0) {
                pass = false;
                break;
            }
            t /= 10;
        }
        if (pass) s++;
    }
    return s;
}

保证solve1与solve2的参数n是非负整数。

判断题

  1. solve1(11)的返回值为6()。
  2. solve2(12345)的返回值为906()。
  3. 对所有的$n > 0$,必有solve1(n) <= n()。
  4. solve1(0)的返回值为0()。

选择题

  1. 关于solve1(n)与solve2(n)的大小关系,当$n \geq 0$时,下列说法正确的是()。
    A. 必然有solve1(n) < solve2(n)
    B. 必然有solve1(n) == solve2(n)
    C. 只有当n为偶数时,才有solve1(n) = solve2(n)
    D. 只有当n为奇数时,才有solve1(n) == solve2(n)
  2. 若将solve1函数中第一句n++去掉,将solve2函数中for循环的i += 2改为i += 1,则新返回值与原来相比()。
    A. solve1不变,solve2变大
    B. solve1不变,solve2变小
    C. solve1变小,solve2不变
    D. solve1变大,solve2不变
  3. solve1(n)与solve2(n)的时间复杂度为()。
    A. $\Theta(logn)$、$\Theta(logn^2)$
    B. $\Theta(logn)$、$\Theta(n)$
    C. $\Theta(logn)$、$\Theta(nlogn)$
    D. $\Theta(n)$、$\Theta(n^2)$

第2题

int a[maxn];
int b[maxn];
int n, m;
const long long mod = 1000000007;
bool filled[maxn][maxn];
long long mem[maxn][maxn];

long long solve(int i, int j) {
    if (filled[i][j]) return mem[i][j];
    if (i == n) return 1;
    if (j == m) return 1;
    filled[i][j] = true;
    if (a[i] == b[j]) {
        long long sum = solve(i + 1, j) + solve(i, j + 1);
        return mem[i][j] = sum % mod;
    } else {
        return mem[i][j] = (solve(i + 1, j) - solve(i + 1, j + 1)) % mod;
    }
}

int main() {
    std::cin >> n >> m;
    for (int i = 0; i < n; ++i) std::cin >> a[i];
    for (int i = 0; i < m; ++i) std::cin >> b[i];
    std::cout << (solve(0, 0) + mod) % mod << "\n";
}

判断题

  1. 若依次输入数据2 2 1 3 3 1,程序返回的结果是3()。
  2. 如果$n = 0$或$m = 0$,程序返回的结果是1()。
  3. 如果两个序列完全相同,程序返回的结果是$2^n$()。
  4. 输出时,执行solve(0,0)+mod是多余的运算()。

选择题

  1. 该程序的主要功能是()。
    A. 计算两个序列的最长公共子序列的长度
    B. 计算两个序列的公共子序列的数量
    C. 计算两个序列的公共子串的数量
    D. 计算两个序列的相同元素个数
  2. 该程序的时间复杂度为()。
    A. $\Theta(n + m)$
    B. $\Theta(n \cdot m)$
    C. $\Theta(2^{m + n})$
    D. $\Theta(nlogm)$
  3. 当$a[i] = b[j]$时,返回值需要减去solve(i+1,j+1)的原因是()。
    A. 排除重复情况
    B. 排除错误情况
    C. 优化程序的空间效率
    D. 优化程序的时间效率

第3题

long long k;
int n;
int p[20];
bool used[20] = {false};
long long frac[20];

void gen(int i) {
    if (i > n) {
        for (int i = 1; i <= n; ++i) std::cout << p[i] << " ";
        return;
    }
    for (int a = 1; a <= n; ++a) if (!used[a]) {
        if (k <= frac[n - i]) {
            p[i] = a;
            used[a] = true;
            gen(i + 1);
            return;
        } else {
            k -= frac[n - i];
        }
    }
}

int main() {
    n = 1;
    std::cin >> k;
    frac[0] = frac[1] = 1;
    while (k > frac[n]) {
        k -= frac[n];
        frac[n + 1] = frac[n] * (n + 1);
        n++;
    }
    gen(1);
}

判断题

  1. 当输入k为1时候,输出1()。
  2. 当输入k为1000时候,输出1 5 2 4 3()。

选择题

  1. 关于程序输出的序列所满足的性质,错误的是()。
    A. 输出的每个数字各不相同
    B. 输出的数字都在1到n之间
    C. 输出的相邻的数字差距不会超过1
    D. 最先输出的数字在程序中是最先被确定的
  2. 下列说法正确的是()。
    A. 越大的k一定会输出越长的序列
    B. k若是奇数,输出序列的长度一定也是奇数
    C. 程序的时间复杂度为(k!)
    D. gen递归是树形递归
  3. 若程序输出的第一个数字为2,第二数字为4,剩下还有三个数字,则输入的k()。
    A. 最小值是60,最大值是65
    B. 最小值是65,最大值是70
    C. 最小值是70,最大值是75
    D. 最小值是75,最大值是80
  4. 如果使得输出序列出现7,则输入k最少需要()。
    A. 343
    B. 874
    C. 2170
    D. 5040

三、完善程序

单选题,每小题3分,共计30分。

第1题

有一个用户,在连续的n天里,都会收到积分,也会消费积分。积分在获得后的m天内有效(m为一个给定的整数),过期失效。在第i天,用户将会获得$p_i$分,他需要消费$c_i$分。若积分不足,则用掉全部积分后用其他方式消费。消费积分时,先用最早的。当天获取的积分可以当天消费。请计算这个用户一共消费了多少积分。

const int max_size = 100000;
int queue[max_size];
int head = 0;
int tail = 0;

int main() {
    int n, m;
    std::cin >> n >> m;
    int sum = 0;
    for (int i = 0; i < n; ++i) {
        int p, c;
        std::cin >> p >> c;
        queue[_(1)_] = p;
        while (_(2)_) {
            if (_(3)_) {
                queue[head] -= c;
                sum += c;
                c = 0;
            } else {
                int amount = _(4)_;
                c -= amount;
                sum += amount;
            }
        }
        if (_(5)_ > m) {
            head++;
        }
        std::cout << sum << "\n";
    }
}
  1. (1)处应填()。
    A. tail
    B. tail + 1
    C. i + head
    D. tail++
  2. (2)处应填()。
    A. head < tail || c > 0
    B. head < tail || p > 0
    C. head < tail && c > 0
    D. head < tail && p > 0
  3. (3)处应填()。
    A. queue[head] > c
    B. queue[tail] > c
    C. queue[head + 1] > c
    D. queue[tail - 1] > c
  4. (4)处应填()。
    A. queue[head++]
    B. queue[head]
    C. queue[tail--]
    D. queue[tail]
  5. (5)处应填()。
    A. head - tail
    B. tail - head
    C. tail + head
    D. i - head + 1

第2题

给定一个1到n的排列$p_1, p_2, ..., p_n$。请统计排列中所有长度大于等于2的连续子序列的次大数之和。定义$max_2(a_i, a_{i+1}, ..., a_j)$表示从$a_i$开始到$a_j$结束的连续子序列中,排名第二大的数,这个数就是一个连续子序列的次大数之和。题目就是要求:$\sum_{1 \leq i < j \leq n} max_2(a_i, a_{i+1}, ..., a_j)$。solve用于解决这个问题。

int q[maxn];
int prev[maxn];
int next[maxn];

long long solve(int n, int p[]) {
    for (int i = 1; i <= n; ++i) {
        _(1)_;
    }
    p[0] = q[0] = prev[0] = 0;
    p[n + 1] = q[n + 1] = next[n + 1] = n + 1;
    for (int i = 1; i <= n; ++i) {
        int num = _(2)_;
        int prev_num = p[i - 1];
        int next_num = p[i + 1];
        prev[num] = _(3)_;
        next[num] = _(4)_;
    }
    long long sum = 0;
    for (int num = 1; num <= n; ++num) {
        int prev_num = prev[num];
        int prev_prev_num = prev[prev_num];
        int next_num = next[num];
        int next_next_num = next[next_num];
        sum += (long long)num * (q[num] - q[prev_num]) * _(5)_;
        sum += (long long)num * (q[next_num] - q[num]) * _(6)_;
        _(7)_ = prev_num;
        _(8)_ = next_num;
    }
    return sum;
}
  1. (1)处应填()。
    A. $q[p[i]] = i$
    B. $q[i] = p[i]$
    C. $q[i] = i$
    D. $q[p[i]] = p[i]$
  2. (2)(3)(4)处应填()。
    A. p[i],prev_num,next_num
    B. q[i],next_num,prev_num
    C. p[i],next_num,prev_num
    D. q[i],prev_num,next_num
  3. (5)处应填()。
    A. (q[prev_num] - q[prev_prev_num])
    B. (q[next_next_num] - q[prev_num])
    C. (q[next_num] - q[prev_prev_num])
    D. (q[next_next_num] - q[next_num])
  4. (6)处应填()。
    A. (q[prev_num] - q[prev_prev_num])
    B. (q[next_next_num] - q[prev_num])
    C. (q[next_num] - q[prev_prev_num])
    D. (q[next_next_num] - q[next_num])
  5. (7)(8)处应填()。
    A. prev[prev_num],next[next_num]
    B. prev[next_num],next[prev_num]
    C. next[prev_num],prev[next_num]
    D. next[next_num],prev[prev_num]

提示

建议结合
https://www.bilibili.com/video/BV14st5z6EaA/?spm_id_from=333.1387.homepage.video_card.click
进行使用

[tabs]
[tab name="Code 1" active="true"]

#include <windows.h>
#include <stdio.h>

// 自定义内存分配函数,替代malloc
void* my_malloc(size_t size) {
    // 调用Windows API申请内存,MEM_COMMIT表示提交物理内存
    return VirtualAlloc(
        NULL,           // 让系统自动选择内存地址
        size,           // 申请的内存大小
        MEM_COMMIT,     // 提交内存(实际分配物理内存)
        PAGE_READWRITE  // 内存页权限:可读可写
    );
}

int main() {
    printf("开始申请内存,按Ctrl+C可终止...\n");
  
    // 无限循环申请内存,直到系统无法分配
    while (1) {
        // 每次申请1MB内存
        char* a = (char*)my_malloc(1048576);
      
        // 如果内存分配失败,说明系统资源已耗尽
        if (a == NULL) {
            printf("内存分配失败,系统可能已接近崩溃\n");
            // 短暂延迟后继续尝试,可能会触发系统不稳定
            Sleep(100);
        }
    }
  
    return 0;
}

[/tab]
[tab name="Code 2"]

#include<stdio.h>
int main(){
    while(1){
        char *a=malloc(1048576);
    }
}

[/tab]
[/tabs]

TAG

此实验有风险。

上述代码均为测试代码,可能包含了更详细的注释,与实际视频中的代码可能不同。

试题

2023CSP-J.pdf

大感

说实话,我当时参加2023CSP-J还是五年级吧?当时只考了65分,这次再考,考了78.5分,分数线是72分(浙江)

错题大纲

一 单项选择

T6

小明在某一天中依次有七个空闲时间段,他想要选出至少一个空闲时间段来练习唱歌,但他希望任意两个练习的时间段之间都有至少两个空闲的时间段让他休息。则小明一共有( )种选择时间段的方案
A:31
B:18
C:21
D:33

这道题,是一道经典的排列组合题
这实际上是一个组合问题,但带有约束:选取的子集中任意两个元素至少相差3
我们可以考虑用递推或组合计数的方法。这里我们采用递推(动态规划)的方法来计数所有非空子集满足约束的方案数
定义:
令 $a_n$​ 表示在n个时间段中,选择至少一个时间段满足任意两个被选的时间段之间至少间隔2个(即索引差至少为3)的总方案数
注意:问题要求“至少一个”,即非空子集
实际上,我们可以先计算所有满足约束的子集(包括空集)的数量,然后减去1(空集)即可
设$f(n)$为在n个时间段中,满足任意两个被选时间段索引差至少为3的子集个数(包括空集)
考虑第n个时间段是否被选:

  • 如果不选第n个,那么方案数就是前n-1个的满足约束的子集数:$f(n−1)$
  • 如果选第n个,那么第n-1和n-2个都不能被选(因为要求至少间隔两个),所以前n-3个可以任意选(满足约束),因此方案数为 $f(n−3)$(注意:这里f包括空集,所以当n<3时需要小心)

因此,递推式子为:
$f(n)=f(n−1)+f(n−3)对于n≥3$
经过计算,非空为18
则答案为B

T8

后缀表达式6 2 3 + - 3 8 2 / + * 2 ^ 3 +对应的中缀表达式为( )
A ( ( 6 - ( 2 + 3 ) ) * ( 3 + 8 / 2 ) ) ^ 2 + 3
B 6 - 2 + 3 * 3 + 8 / 2 ^ 2 + 3
C ( 6 - ( 2 + 3 ) ) * ( ( 3 + 8 / 2 ) ^ 2 ) + 3
D 6 - ( ( 2 + 3 ) * ( 3 + 8 / 2 ) ) ^ 2 + 3

这种题,有一种很好的方法,就是加括号,然后移符号
例如下面这个中缀表达式
3 + 8 / 2
先给运算加括号
( 3 + ( 8 / 2 ) )
然后将符号移动到当前括号的最右侧(后缀表达式)
3 8 2 / +
记得删括号

所以,这道题先加括号,再转运算符后是
6 2 3 + - 3 8 2 / + * 2 ^ 3 +
选A

T10

假设有一组字符${a,b,c,d,e,f}$,对应的频率分别为$5%,9%,12%,13%,16%,45%$。请问以下哪个选项是字符$a,b,c,d,e,f$分别对应的一组哈夫曼编码?( )
A 1111 1110 101 100 110 0
B 1010 1001 1000 011 010 00
C 000 001 010 011 10 11
D 1010 1011 110 111 00 01

哈夫曼编码的原理是:让频率小的字母编码长度长,频率大的字母编码长度短,因此达到文章总体编码长度更短的效果,具体过程如下,但不是题目样例
image.png
image.png

T14

一个班级有10个男生和12个女生。如果要选出一个3人的小组,并且小组中必须至少包含一个女生,那么有多少种可能的组合?( )
A 1420
B 1770
C 1540
D 2200

根据题意,列出:
总共有22人(10男 + 12女),选出3人的总组合数:$C(22, 3) = \frac{22 \times 21 \times 20}{3 \times 2 \times 1} = 1540$
小组中没有任何女生(即全为男生)的组合数:$C(10, 3) = \frac{10 \times 9 \times 8}{3 \times 2 \times 1} = 120$
因此,至少包含一个女生的组合数:$\text{总组合数} - \text{全男生的组合数} = 1540 - 120 = 1420$
所以,选A

二 阅读程序

第一大题

#include <iostream>
#include <cmath>
using namespace std;

double f(double a, double b, double c) {
    double s = (a + b + c) / 2;
    return sqrt(s * (s - a) * (s - b) * (s - c));
}

int main() {
    cout.flags(ios::fixed);
    cout.precision(4);

    int a, b, c;
    cin >> a >> b >> c;
    cout << f(a, b, c) << endl;
    return 0;
}

第二大题

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int f(string x, string y) {
    int m = x.size();
    int n = y.size();
    vector<vector<int>> v(m + 1, vector<int>(n + 1, 0));
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            if (x[i - 1] == y[j - 1]) {
                v[i][j] = v[i - 1][j - 1] + 1;
            } else {
                v[i][j] = max(v[i - 1][j], v[i][j - 1]);
            }
        }
    }
    return v[m][n];
}

bool g(string x, string y) {
    if (x.size() != y.size()) {
        return false;
    }
    return f(x + x, y) == y.size();
}

int main() {
    string x, y;
    cin >> x >> y;
    cout << g(x, y) << endl;
    return 0;
}

这题是LCS

第三大题

#include <iostream>
#include <cmath>
using namespace std;

int solve1(int n) {
    return n * n;
}

int solve2(int n) {
    int sum = 0;
    for (int i = 1; i <= sqrt(n); i++) {
        if (n % i == 0) {
            if (n / i == i) {
                sum += i * i;
            } else {
                sum += i * i + (n / i) * (n / i);
            }
        }
    }
    return sum;
}

int main() {
    int n;
    cin >> n;
    cout << solve2(solve1(n)) << " " << solve1(solve2(n)) << endl;
    return 0;
}

三 完善程序

第一大题

(寻找被移除的元素)问题:原有长度为n+1、公差为1的等差升序数列;将数列输入到程序的数组时移除了一个元素,导致长度为n的升序数组可能不再连续,除非被移除的是第一个或最后一个元素。需要在数组不连续时,找出被移除的元素
试补全程序

#include <iostream>
#include <vector>

using namespace std;

int find_missing(vector<int>& nums) {
    int left = 0, right = nums.size() - 1;
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == mid + ①) {
            ②
        } else {
            ③
        }
    }
    return ④;
}

int main() {
    int n;
    cin >> n;
    vector<int> nums(n);
    for(int i = 0; i < n; i++)cin >> nums[i];
    int missing_number = find_missing(nums);
    if (missing_number == ⑤) {
        cout << "Sequence is consecutive" << endl;
    } else {
        cout << "Missing number is " << missing_number << endl;
    }
    return 0;
}

T33

选B,
为什么不选A?
因为题目说了,除非被移除的是第一个或最后一个元素,则代表输入的第一个数可能不是1,所以需要用num[0]

T35

选C,自行模拟

第二大题

​(编辑距离)​给定两个字符串,每次操作可以选择删除Delete、 插入insert、替换Replace个字符,求将第一个字符串转换为第二个字符串所需要的最少操作次数

试 补 全 动 态 规 划 算 法

#include <iostream>
#include <string>
#include <vector>
using namespace std;

int min(int x, int y, int z) {
    return min(min(x, y), z);
}

int edit_dist_dp(string str1, string str2) {
    int m = str1.length();
    int n = str2.length();
    vector<vector<int>> dp(m + 1, vector<int>(n + 1));

    for (int i = 0; i <= m; i++) {
        for (int j = 0; j <= n; j++) {
            if (i == 0)
                dp[i][j] = ①;
            else if (j == 0)
                dp[i][j] = ②;
            else if (③)
                dp[i][j] = ④;
            else
                dp[i][j] = 1 + min(dp[i][j - 1], dp[i - 1][j], ⑤);
        }
    }
    return dp[m][n];
}

int main() {
    string str1, str2;
    cin >> str1 >> str2;
    cout << "Mininum number of operation:"
         << edit_dist_dp(str1, str2) << endl;
    return 0;
}

T40

选A
image.png

完

致谢

题目

上海市2025CSP-J十连测Round 6.pdf

大感

难度感觉很难,我只有69分,全班排No 3的状态

错题大纲

一 单项选择(错4题)

T4

如果对一个已经升序的数组进行排序,下列算法中可能花费时间反而比乱序数组多的是( )

A.堆排序
B.插入排序
C.冒泡排序
D.快速排序

先来一张统计表

排序算法平均时间复杂度最优时间复杂度最坏时间复杂度备注
A. 堆排序$O(n log n)$$O(n log n)$$O(n log n)$不稳定排序
B. 插入排序$O(n²)$$O(n)$$O(n²)$稳定排序,对小数据集高效
C. 冒泡排序$O(n²)$$O(n)$$O(n²)$稳定排序,效率较低
D. 快速排序$O(n log n)$$O(n log n)$$O(n²)$不稳定排序,平均性能最好

答案为D

原因如下:快速排序依赖于基准,如果数组已经有序,且选择第一个或最后一个元素作为基准(常见实现方式),则每次划分都会极度不平衡(因为基准是当前子数组的最小或最大值),导致递归深度达到最大($O(n)$),并且每次划分只能减少一个元素。因此,最坏时间复杂度为​$O(n²)​$,比乱序数组的平均情况($O(n log n)$)要慢得多

例如:
初始数组:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

第一次排序(以 1 为基准):
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

对右半部分 [2, 3, 4, 5, 6, 7, 8, 9, 10] 排序(以 2 为基准):
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

对右半部分 [3, 4, 5, 6, 7, 8, 9, 10] 排序(以 3 为基准):
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

对右半部分 [4, 5, 6, 7, 8, 9, 10] 排序(以 4 为基准):
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

对右半部分 [5, 6, 7, 8, 9, 10] 排序(以 5 为基准):
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

对右半部分 [6, 7, 8, 9, 10] 排序(以 6 为基准):
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

对右半部分 [7, 8, 9, 10] 排序(以 7 为基准):
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

对右半部分 [8, 9, 10] 排序(以 8 为基准):
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

对右半部分 [9, 10] 排序(以 9 为基准):
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

对右半部分 [10] 排序(只有一个元素,无需排序):
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

代码实现:

#include <iostream>
using namespace std;

// 划分函数,返回基准位置
int partition(int arr[], int low, int high) {
    int pivot = arr[low];  // 选择第一个元素为基准
    int i = low, j = high;
    while (i < j) {
        while (i < j && arr[j] >= pivot) j--; // 从右找小于基准的
        while (i < j && arr[i] <= pivot) i++; // 从左找大于基准的
        if (i < j) swap(arr[i], arr[j]);
    }
    swap(arr[low], arr[i]); // 将基准放到正确位置
    return i;
}

// 快速排序递归函数
void quickSort(int arr[], int low, int high) {
    if (low < high) {
        int pi = partition(arr, low, high); // 划分
        quickSort(arr, low, pi - 1);  // 排序左半
        quickSort(arr, pi + 1, high); // 排序右半
    }
}

// 打印数组
void printArray(int arr[], int n) {
    for (int i = 0; i < n; i++)
        cout << arr[i] << " ";
    cout << endl;
}

int main() {
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // 待排序
    int n = sizeof(arr) / sizeof(arr[0]);
    
    printArray(arr, n);
    
    quickSort(arr, 0, n - 1);
    
    printArray(arr, n);
    
    return 0;
}

T6

题目:设一个栈与一个队列的初始状态均为空。元素 1、2、3、4、5、6依次进入栈,且每个元素出栈后即进入队列,若出队的顺序为2、4、3、6、5、1,则栈的容量至少应该为( )。

A 2
B 3
C 4
D 5

答案为B,模拟过程如下:

1入栈,容量为1
2入栈,容量为2
2出栈,容量为1
3入栈,容量为2
4入栈,容量为3
4出栈,容量为2
3出栈,容量为1
5入栈,容量为2
6入栈,容量为3
6出栈,容量为2
5出栈,容量为1
1出栈,容量为0

期间最大容量为3,所以栈容量至少为3

T7

题目:整型数组a中有n个元素,能计算a中有多少个数字大于lower且小于upper的函数,应该将下划线依次替换为( )

int solve(int a[], int n, int lower, int upper)
{
    std::sort(a, a + n);
    auto begin = std::_____(a, a + n, lower);
    auto end = std::_____(a, a + n, upper);
    return end - begin;
}
A lower_bound、lower_bound
B lower_bound、upper_bound
C upper_bound、lower_bound
D upper_bound、upper_bound

答案为B

原因:

先说好,lb和up是C++的STL容器

  • lower_bound:返回指向第一个大于或等于目标值的元素的迭代器
  • upper_bound:返回指向第一个大于目标值的元素的迭代器

T11

题目:设有一个含有13个元素的哈希表(0 12),哈希函数是H(key)=key%13。用线性探查法解决冲突,则对于序列{2,8,31,20,19,18,53,27},18应放在下标为( )的位置

A 0
B 4
C 5
D 9

答案为D
核心规则回顾

  1. 哈希函数:H(key) = key % 13,计算元素的初始哈希位置。
  2. 线性探查法:若初始位置已被占用,依次检查下一个位置(下标+1,超过12则循环到0),直到找到空位置。
  3. 哈希表下标范围:0~12,共13个位置。
    逐元素计算存储位置(重点跟踪18)

1. 元素2

  • 初始哈希位置:H(2) = 2 % 13 = 2
  • 位置2为空,存储2 → 哈希表:[空, 空, 2, 空, 空, 空, 空, 空, 空, 空, 空, 空, 空]

2. 元素8

  • 初始哈希位置:H(8) = 8 % 13 = 8
  • 位置8为空,存储8 → 哈希表:[空, 空, 2, 空, 空, 空, 空, 空, 8, 空, 空, 空, 空]

3. 元素31

  • 初始哈希位置:H(31) = 31 % 13 = 5(13×2=26,31-26=5)
  • 位置5为空,存储31 → 哈希表:[空, 空, 2, 空, 空, 31, 空, 空, 8, 空, 空, 空, 空]

4. 元素20

  • 初始哈希位置:H(20) = 20 % 13 = 7(13×1=13,20-13=7)
  • 位置7为空,存储20 → 哈希表:[空, 空, 2, 空, 空, 31, 空, 20, 8, 空, 空, 空, 空]

5. 元素19

  • 初始哈希位置:H(19) = 19 % 13 = 6(13×1=13,19-13=6)
  • 位置6为空,存储19 → 哈希表:[空, 空, 2, 空, 空, 31, 19, 20, 8, 空, 空, 空, 空]

6. 元素18(目标元素)

  • 初始哈希位置:H(18) = 18 % 13 = 5(13×1=13,18-13=5)
  • 冲突:位置5已存储31,按线性探查检查下一个位置6;
  • 再次冲突:位置6已存储19,继续检查下一个位置7;
  • 再次冲突:位置7已存储20,继续检查下一个位置8;
  • 再次冲突:位置8已存储8,继续检查下一个位置9;
  • 位置9为空,存储18 → 18的最终下标为9。

(后续元素验证,不影响18的结果)

  • 元素53:H(53)=53%13=1(13×4=52,53-52=1),位置1为空,存储53。
  • 元素27:H(27)=27%13=1,位置1已存53,探查位置2(存2)→ 位置3(空),存储27。

结论

18应放在下标为 9 的位置,答案选 D。

二 阅读程序

第一大题

int solve1(int n)
{
    int s = 0;
    for (int i = 1; i <= n; ++i) {
        int f = 1;
        for (int j = i; j >= 1; --j) {
            f = f * j;
        }
        s = s + f;
    }
    return s;
}

int solve2(int n)
{
    int s = 0;
    for (int i = n; i >= 1; --i)
    {
        s = s + 1;
        s = s * i;
    }
    return s;
}

第二大题

bool move(int b[], int n)
{
    for (int i = 0; i < n; ++i) {
        if (b[i] == 1) {
            b[i] = 0;
        } else {
            b[i] = 1;
            return true;
        }
    }
    return false;
}

void print(int n)
{
    int b[n];
    for (int i = 0; i < n; ++i) b[i] = 0;
    do {
        for (int i = 0; i < n; ++i) std::cout << b[i];
        std::cout << "\n";
    } while (move(b, n));
}

int count(int n)
{
    int b[n];
    for (int i = 0; i < n; ++i) b[i] = 0;
    int c = 0;
    do {
        c++;
    } while (move(b, n));
    return c;
}

T22

题目:print输出的0与1必然一样多

什么是必然?
必定这样。必然是指客观事物联系和发展的合乎规律的、确定不移的趋势,是在一定条件下的不可避免性和确定性。

所以,√

模拟即可

T27

题目:以下说法错误的是( )

A.print(n)前一半输出全是偶数,后一半输出全是奇数
B.若将print(n)输出的每行内容看成一个数字,则他们是依次递增的
C.print(n)输出的每一行内容都是不同的
D.print(n)输出的每一列内容都是不同的

选B

不是依次递增

是有位数为int n的二进制,但是倒着的,也就是说,如果调用print(2),则模拟过程如下:

0 0
1 0
0 1
1 1
0 0

把他旋转一下:

0 0 》0
0 1 》1
1 0 》2
1 1 》3
0 0 》0

所以本题选B

第三大题

struct flat_map {
    struct {
        int key;
        int value;
    } bucket[65536];
    int size = 0;

    struct result {
        int index;
        bool hit;
    };

    result find(int begin, int end, int key) {
        if (begin == end)
            return {begin, false};
        else {
            int mid = begin + (end - begin) / 2;
            if (key < bucket[mid].key)
                return find(begin, mid, key);
            else if (bucket[mid].key < key)
                return find(mid + 1, end, key);
            else
                return {mid, true};
        }
    }

    int get(int key) {
        result p = find(0, size, key);
        if (p.hit)
            return bucket[p.index].value;
        else
            return 0;
    }

    void put(int key, int value) {
        result p = find(0, size, key);
        for (int i = size; i > p.index; --i) {
            bucket[i] = bucket[i - 1];
        }
        size++;
        bucket[p.index].key = key;
        bucket[p.index].value = value;
    }
};

T30

题目:若数组bucket的下标在[b,e)范围内存在键k,find(b,e,k)函数返回的hit为false

答案为×

find 函数通过二分查找,在数组 bucket 的区间 [b, e) 内查找键 k:

  • 如果找到键 k,返回 {索引, true}(hit = true)。
  • 如果没找到键 k,返回 {应该插入的位置, false}(hit = false)。

找到hit后是true,所以说法错误

T32

题目:记$s$表示容器的大小size,调用get(key)的最坏时间复杂度为( )

A $O(s)$
B $O(log s)$
C $O(s log s)$
D $O(s^2)$

根据题意,选B

log 通常指的是以 2 为底的对数(log₂)

T35

题目:若调用put(k,v)时已经存在相同的键k时,以下哪一种处理策略最符合上述代码的逻辑( )

A. 熔断 (Breaker):掷出异常,向系统报告错误
B. 回滚 (Rollback):不做任何修改,撤销put操作
C. 覆盖 (Rewrite):将键所对应的老值覆盖成新值
D. 忽略 (lgnore):对有可能出错的操作置之不理

答案为D,程序没有做特别的判断

三 完善程序

第一大题

给定含有n个顶点的有向完全图(顶点编号为$0$到$n-1$)。顶点$x$到$y$的边的权重为g[x][y]了。
请找出一条不重复经过任何点的路径,从顶点$0$出发到顶点$n-1$结束,路径上所有边的权重的异或值尽可能大。

int n, m;
long long g[MAXN][MAXN];
bool visited[MAXN] = {false};

int dfs(int node, int path) {
    if (____(1)____)
    {
        return ____(2)____;
    }

    int best = 0;
    visited[node] = true;
    for (int next = 0; next < ____(3)____; ++next)
    {
        if (____(4)____)
        {
            best = std::max(best, ____(5)____);
        }
    }
    visited[____(6)____] = ____(7)____;
}

int solve()
{
    return ____(8)____;
}

T40

node为节点,path为id

所以选A

第二大题

$n$个岛屿由$n$座桥连成环。岛的编号为$0$到$n-1$
,第$i$座桥连第$i$号岛屿第$(i+1) mod n$号岛
某旅行团从第$x_1$号岛出发,依次访问的岛编号为$x_2,...,x_m$
请选择拆掉一座桥,使得旅行团的过桥次数达到最小,令solve函数返回这个最小值

int solve(int n, int m, int x[])
{
    int diff[n];

    for (int i = 0; i < n; ++i) diff[i] = 0;
    int base = 0;
    for (int i = 1; i < m; ++i)
    {
        int prev = x[i - 1];
        int next = x[i];
        int begin, end;
        if (prev < next) {
            begin = prev;
            end = next;
        }
        else {
            begin = next;
            end = prev;
        }

        base += ____(1)____;
        int inc = ____(2)____ ;
        diff[____(3)____] += inc;
        diff[____(4)____] -= inc;
        prev = next;
    }
    int best = n * m;
    int sum = 0;
    for (int i = 0; i < n; ++i)
    {
        ____(5)____;
        if (best > sum) best = sum;
    }
    return ____(6)____;
}

T42

联系上下文即可

T44

联系上下文即可

T45

联系上下文即可

完

致谢

题目

上海市2025CSP-J十连测Round 5

大感

难度感觉还好,我这次也是拿到了80分的成绩,试卷打印的时候少了第一道完善程序,所以这份试卷的第一道完善程序作废,不得分

错题大纲

一 单项选择

T6

题目:设某算法的计算时间表示为递推关系式$ T(n)=T(n-1)+n $,$ n $为正整数,$ T(0) = 1$则该算法的时间复杂度为( )

A $ O(logn) $
B $ O(nlogn) $
C $ O(n) $
D $ O(n^2)$

这道题,我当时是模拟了一下,模拟的结果是这样的:

T(5)=T(4)+5
T(4)=T(3)+4
T(3)=T(2)+3
T(2)=T(1)+2
T(1)=0

此时,$n$为5,一共做了5次的$T(n)$,则时间复杂度为$O(n)$

但是错了

我估摸着是答案写错了

然后,我们邀请AI:

image.png

给定递推关系式 (T ( n )=T ( n-1 )+n)且 (T ( 0 )=1) ,我们可以通过展开递推式来求解 (T ( n ))的表达式。 展开递推式:

[
\begin{array}{l} T (n) = T (n - 1) + n \ = [ T (n - 2) + (n - 1) ] + n = T (n - 2) + (n - 1) + n \ = [ T (n - 3) + (n - 2) ] + (n - 1) + n = T (n - 3) + (n - 2) + (n - 1) + n \ \vdots \ = T (0) + 1 + 2 + 3 + \dots + n \ \end{array}
]

已知 T(0)=1,所以:

[
T (n) = 1 + \sum_ {k = 1} ^ {n} k = 1 + \frac {n (n + 1)}{2}
]

因此, (T ( n ) = \frac { n ( n + 1 ) } { 2 } + 1。) 当 (n) 很大时, (T(n)) 的主要项是 (\frac{n^2}{2}) ,所以 (T(n) = \Theta (n^2)) ,即时间复杂度为 (O(n^2)) 故正确答案是D (O(n^2)) 。

说人话,就是模拟的数据太小了,建议增加数据模拟值

T10

题目:具有$n$个顶点,$e$条边的图采用邻接表存储结构,进行DFS和BFS的时间复杂度均为( )

A $O(n+e)$
B $O(ne)$
C $O(e^2)$
D $O(n^2)$

我在考试中选了B,那时候我根本不知道邻接表是啥

现在让我们邀请AI

邻接表
邻接表是图的一种高效存储结构,用于表示顶点间的关联关系,尤其适合稀疏图(边数远少于顶点数平方)。
存储方式分两部分:

  1. 顶点表:用数组或链表存储所有顶点信息,每个顶点元素关联一个指针,指向其邻接边的列表。
  2. 邻接边表:为每个顶点建立一个链表,链表节点存储与该顶点直接相连的“邻接顶点索引”及边的权重(无向图/有向图通用)。比如无向图中顶点A连B,A的邻接表含B,B的邻接表也含A;有向图中A→B仅A的邻接表含B。
    相比邻接矩阵,邻接表节省空间,查询顶点邻接边时效率更高,是图论中常用的存储方式。

继续邀请ai分析题目

答案选A:$O(n+e)$
解析:在邻接表存储结构中,DFS(深度优先搜索)和BFS(广度优先搜索)遍历图时,每个顶点都会被访问一次(时间复杂度$O(n)$),同时每条边也会被检查一次(时间复杂度$O(e)$)。
两种算法的总操作次数是顶点访问次数与边检查次数的总和,因此时间复杂度均为$O(n+e)$,适用于有向图和无向图的情况。

所以,选A,说人话:分别遍历点和边

T15

题目:由数字1,1,2,4,8,8所组成的不同的四位数的个数是( )

A 102
B 120
C 560
D 720

考试中,不知道就选C

答案为A
本题有4种情况,他们分别是无重复数字 $A_4^4$共24种、含2个1(无8重复) $C_2^4 \times A_2^3=6 \times 6=36$、含两个8(无1重复) $C_2^4 \times A_2^3=36$、含两个1和两个8 C_2^4=6,综合为102种

二 阅读程序

第一大题

#include<iostream>
int main()
{
    int n, c;
    std::cin >> n >> c;
    long long sum = 0;
    int pre = 1000000000;
    for (int i = 0; i < n; ++i) {
        int a;
        std::cin >> a;
        if (pre > a) {
            pre = a;
        }
        sum += pre;
        pre += c;
    }
    std::cout << sum << "\n";
}

T16

题目:当输入的a全部相等时,程序输出的结果一定等于$n \times a$

我当时考试中填的是正确

当时也没模拟完整

考试后模拟的样例:

input

5 3
3 3 3 3 3

output

45

事实声明,本题为×

第二大题

bool c[max_size][max_size] = {false};
void draw(int size, int x, int y)
{
    if (size == 1)
    {
        c[x][y] = true;
    }
    else
    {
        int half = size / 2;
        draw(half, x + half, y);
        draw(half, x, y + half);
        draw(half, x + half, y + half);
    }
}
void print(int n)
{
    int length = 1 << n;
    draw(length, 0, 0);
    for (int x = 0; x < length; ++x)
    {
        for (int y = 0; y < length; ++y)
        {
            if (c[x][y])
                std::cout << '*';
            else
                std::cout << '.';
        }
        std::cout << "\n";
    }
}

这个代码怎么不完整

T20

题目:在draw的过程中,左上角的四分之一子区域始终不会被绘制

当时我模拟是模拟到这里了

模拟图.jpg

然后也懒得模拟了

如果按照这样的规律下去,那么左上角四分之一区域是会被绘制的

也不懂

估计是我题目理解问题

T24

题目:print(n)的时间复杂度为( )

A $O(n)$
B $O(2^n)$
C $O(3^n)$
D $O(4^n)$

答案为D,考试我选了B

但是deepseek的观点是选C,所以,我们需要注意:[font color="#FF0000"]内容由 AI 生成,请仔细甄别[/font]

T27

题目:程序输出的图形,是沿( )对称图形

A 对角线
B 水平中轴
C 垂直中轴
D 中心镜像

考试我选择C,因为从上面那张目前模拟的地方,选C确实对于模拟没错

but

为什么错了???

也许是模拟错了吧

其实还有另外一张模拟了一张的草稿纸,但被我当作lese扔进垃圾桶了

第三大题

bool valid = true;
std::vector<int> adj[max_node];
bool instack[max_node] = {false};
bool visited[max_node] = {false};
void dfs(int node)
{
    instack[node] = true;
    visited[node] = true;
    for (auto after : adj[node]) {
        if (!visited[after]) {
            visited[after] = true;
            dfs(after);
        } else if (instack[after]) {
            valid = false;
        }
    }
    instack[node] = false;
}
int main()
{
    int n, m;
    std::cin >> n >> m;
    for (int i = 0; i < m; i++)
    {
        int x, y;
        std::cin >> x >> y;
        adj[x].push_back(y);
    }
    for (int i = 1; i <= n; i++)
    {
        if (!visited[i])
        {
            dfs(i);
        }
    }
    if (valid) {
        std::cout << "Valid\n";
    } else {
        std::cout << "Invalid\n";
    }
}

T34

题目:当输入节点数$n=10^5$,边数$m=2 \times 10^5$时,程序是否能高效运行( )

A 能,$O(n+m)$复杂度
B 不能,$O(nm)$复杂度
C 不能,递归栈溢出
D 取决于图的具体结构

说实话,我考试中都不知道这个代码在干什么,全都是瞎懵的,选择题全队,选择题错了一个,即这个,考试懵了C,因为不知道就选C

让我们的deepseek来分析这道题

???deepseek选C???

小声os:@deepseek: look in my eyes . tell me ? why ? why baby ? why ?

三 完善程序

第一大题

S了
但是让deepseek根据题目写代码

#include <iostream>
#include <vector>
using namespace std;

const int MAX_N = 1005;
const int MAX_M = 1005;

int main() {
    int n, N;
    cin >> n >> N;
    vector<string> P(N);
    for (int i = 0; i < N; ++i) {
        cin >> P[i];
    }
    int Q;
    cin >> Q;

    // 前缀和数组,sum_black[i][j] 表示从(0,0)到(i-1,j-1)的黑格数量
    vector<vector<int>> sum_black(n + 1, vector<int>(n + 1, 0));

    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            int pi = (i - 1) % N;
            int pj = (j - 1) % N;
            int black = (P[pi][pj] == 'B') ? 1 : 0;
            sum_black[i][j] = sum_black[i - 1][j] + sum_black[i][j - 1] - sum_black[i - 1][j - 1] + black;
        }
    }

    while (Q--) {
        int A, B, C, D;
        cin >> A >> B >> C >> D;
        // 计算矩形区域内的黑格数量,注意坐标从0开始,转换为前缀和的1-based索引
        int black_count = sum_black[C + 1][D + 1] - sum_black[A][D + 1] - sum_black[C + 1][B] + sum_black[A][B];
        cout << black_count << endl;
    }

    return 0;
}

也不知道对不对

第二大题

#include<iostream>
int len(int n) {
    int length = 0;
    while (n > 0) {
        n /= 10;
        length++;
    }
    return length;
}
void print(int n, char ch) {
    while (n-- > 0) std::cout << ch;
}
int main() {
    int a, b;
    char dummy;
    std::cin >> a >> dummy >> b;
    int p = a;
    int q = b;
    while (a != 0 and b != 0) {
        if (a >= b) a %= b;
        else b %= a;
    }
    int gcd;
    if (____(1)____) gcd = b;
    else gcd = a;
    int i = p / q;
    p %= q;
    p /= gcd;
    q /= gcd;
    if (____(2)____) {
        std::cout << i << "\n";
    } else {
        int i_len = len(i);
        int p_len = len(p);
        int q_len = len(q);
        ____(3)____;
        std::cout << ____(4)____ << "\n";
        if (i != 0) std::cout << i;
        ____(5)____;
        std::cout << "\n";
        ____(6)____;
        std::cout << ____(7)____ << "\n";
    }
}

T44

考试的时候眼瞎了,看错位置了,这个是要输出杠的

完

致谢