data-structure-experiments/report.md

3484 lines
96 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#! https://zhuanlan.zhihu.com/p/672958409
# 数据结构实验报告
## Author: Luthics
# 实验一 背包问题求解
## 问题描述
假设有一个能装入总体积为`T`的背包和`n`件体积分别为`w1,w2,…wn`的物品,能否从`n`件物品中挑选若干件恰好装背包,即使`w1+w2+…+wm=T`,要求找出所有满足上述条件的解。
例如:当`T=10`,各件物品的体积`{1,8,4,3,5,2}`时,可找到下列`4`组解:
- `(1,4,3,2)`
- `(1,4,5)`
- `(8,2)`
- `(3,5,2)`
## 实现提示
可利用回溯法的设计思想来解决背包问题。首先,将物品排成一列,然后,顺序选取物品装入背包,若已选取`i`件物品后未满,则继续选取第`i+1`件,若该件物品“太大”不能装入,则弃之,继续选取下一件,直至背包装满为。
如果在剩余的物品中找不到合适的物品以填满背包,则说明“刚刚”装入的物品“不合适”,应将它取出“弃之一”,继续再从“它之后”的物品中选取,如此重复,直到求得满足条件的解,或者无解。
由于回溯求解的规则是“后进先出”,自然要用到“栈”。
进一步考虑:如果每件物品都有体积和价值,背包又有大小限制,求解背包中存放物品总价值最大的问题解---最优解或近似最优解。
## 思路
一个能装入总体积为`T`的背包和`n`件体积分别为`w1,w2,…wn`的物品,能否从`n`件物品中挑选若干件恰好装背包,即使`w1+w2+…+wm=T`
相当于取一个向量 $n$ = $(x_1, x_2, ..., x_n)$,其中 $x_i \in \{0, 1\}$,使得 $\sum_{i=1}^n x_i w_i = T$,求所有满足条件的 $n$。
使用暴力 `dfs` 搜索,搜索每一件物品的选择情况,符合条件时输出。
使用 `sum+w[k]<=T` 进行剪枝,提高运行速度
总情况数为 $2^n$,时间复杂度为 $O(2^n)$。
## 代码及解释
```c++
#include <stdio.h>
#include <stdlib.h>
#define MAX_N 100
int T;
int n;
int w[MAX_N];
int x[MAX_N];
int solution_count = 0;
// dfs函数t为当前搜索的层数sum为当前已经选择的物品的体积和k为当前搜索的物品编号
void dfs(int t, int sum, int k) {
if (sum == T) {
solution_count++;
if (solution_count > 50)
return;
// 输出解
int *re = malloc(sizeof(int) * k);
int j = 0;
for (int i = 0; i < k; i++) {
if (x[i]) {
re[j++] = w[i];
}
}
for (int i = 0; i < j; i++) {
printf("%d ", re[i]);
}
printf("\n");
} else if (sum < T && k < n) {
// 剪枝提高运行速度
if (sum + w[k] <= T) {
// 选第k件物品
x[k] = 1;
dfs(t + 1, sum + w[k], k + 1);
}
// 不选第k件物品
x[k] = 0;
dfs(t + 1, sum, k + 1);
}
}
int main() {
freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
scanf("%d", &T);
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &w[i]);
}
// 搜索
dfs(0, 0, 0);
printf("Total Solution Count: %d\n", solution_count);
return 0;
}
```
## 输入样例
```
10
6
1 8 4 3 5 2
```
## 输出样例
```
1 4 3 2
1 4 5
8 2
3 5 2
Total Solution Count: 4
```
## 拓展方法
将题目拓展为背包问题,使用 `dp` 算法解决
### 代码
```c++
#include <stdio.h>
#include <stdlib.h>
int T, M, t[105], w[105];
int dp[1005][1005];
int max(int a, int b) { return a > b ? a : b; }
int main() {
freopen("dp.in", "r", stdin);
scanf("%d%d", &T, &M);
for (int i = 1; i <= M; i++) {
scanf("%d%d", &t[i], &w[i]);
}
dp[0][0] = 0;
// dp[i][j]表示前i件物品体积为j时的最大价值
for (int i = 1; i <= M; i++) {
for (int j = 1; j <= T; j++) {
// 如果第i件物品的体积大于j那么就不能选第i件物品
if (j < t[i])
dp[i][j] = dp[i - 1][j];
else
// 否则就是选或者不选第i件物品的最大值
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - t[i]] + w[i]);
}
}
printf("%d\n", dp[M][T]);
// for(int i=1;i<=M;i++){
// for(int j=1;j<=T;j++){
// printf("%d ",dp[i][j]);
// }
// printf("\n");
// }
return 0;
}
```
### 输入样例
```
10 6
1 1
8 4
4 2
3 2
5 3
2 2
```
### 输出样例
```
7
```
# 实验二 农夫过河问题的求解
## 问题描述
一个农夫带着一只狼一只羊和一棵白菜身处河的南岸他要把这些东西
全部运到北岸他面前只有一条小船船只能容下他和一件物品另外只有农夫
才能撑船如果农夫在场则狼不能吃羊羊不能吃白菜否则狼会吃羊羊会
吃白菜所以农夫不能留下羊和白菜自己离开也不能留下狼和羊自己离开
狼不吃白菜请求出农夫将所有的东西运过河的方案
## 实现提示
求解这个问题的简单方法是一步一步进行试探每一步搜索所有可能的选择
对前一步合适的选择后再考虑下一步的各种方案要模拟农夫过河问题首先
需要对问题中的每个角色的位置进行描述可用4位二进制数顺序分别表示农夫
白菜和羊的位置用0表在南岸1表示在北岸例如整数5 (0101)表示农
夫和白菜在南岸而狼和羊在北岸
现在问题变成从初始的状态二进制0000(全部在河的南岸)出发寻找一种
全部由安全状态构成的状态序列它以二进制1111(全部到达河的北岸)为最终目
总状态共16种(0000到1111)(或者看成16个顶点的有向图)可采用广度优先或深度优先的搜索策略---得到从0000到1111的安全路径
以广度优先为例整数队列---逐层存放下一步可能的安全状态Visited[16]数组标记该状态是否已访问过若访问过则记录前驱状态值---安全路径
最终的过河方案应用汉字显示出每一步的两岸状态
## 思路
使用 `dfs` 搜索搜索每一步的选择情况符合条件时输出
## 代码及解释
```c++
#include <stdio.h>
#include <stdlib.h>
int state = 0;
int v[16];
char labels[4][16] = {"农夫", "", "", ""};
int re[16];
int xx[16];
int k = 0;
void pt(int command, int state) {
int *f = malloc(sizeof(int) * 4);
for (int i = 0; i < 4; i++) {
// 计算每一位的值
f[i] = (state >> i) & 1;
}
// 按照命令输出
if (command == 0) {
printf("农夫自己过河\n");
} else if (command == 1) {
printf("带 狼 过河\n");
} else if (command == 2) {
printf("带 羊 过河\n");
} else if (command == 3) {
printf("带 菜 过河\n");
}
printf("A > ");
for (int i = 0; i < 4; i++) {
if (f[i] == 1) {
printf("%s ", labels[i]);
}
}
printf("\n");
printf("B > ");
for (int i = 0; i < 4; i++) {
if (f[i] == 0) {
printf("%s ", labels[i]);
}
}
printf("\n---------------------\n");
}
int validate_state(int state) {
int *f = malloc(sizeof(int) * 4);
for (int i = 0; i < 4; i++) {
f[i] = (state >> i) & 1;
// printf("%d ", f[i]);
}
// printf("\n");
if (!f[1] ^ f[2] && f[0] ^ f[1]) {
return 0;
} else if (!f[2] ^ f[3] && f[0] ^ f[2]) {
return 0;
} else {
return 1;
}
}
int trans(int x, int undo) {
// 0 表示农夫自己过河
// 1 表示带狼过河
// 2 表示带羊过河
// 3 表示带菜过河
int tmp_state = state;
if (x == 1) {
tmp_state = state ^ 3;
} else if (x == 2) {
tmp_state = state ^ 5;
} else if (x == 3) {
tmp_state = state ^ 9;
} else {
tmp_state = state ^ 1;
}
// pt(state);
// printf("%d > ", x);
// pt(tmp_state);
if (undo) {
v[tmp_state] = 0;
state = tmp_state;
re[--k] = 0;
xx[k] = 0;
return 1;
}
if (validate_state(tmp_state) == 0) {
return 0;
}
if (v[tmp_state] == 0) {
v[tmp_state] = 1;
state = tmp_state;
xx[k] = x;
re[k++] = state;
return 1;
}
return 0;
}
void dfs() {
// printf(">> %d\n", state);
if (state == 15) {
for (int i = 0; i < k; i++) {
pt(xx[i], re[i]);
}
printf("\n");
return;
}
if (trans(0, 0)) {
dfs();
trans(0, 1);
}
if (trans(1, 0)) {
dfs();
trans(1, 1);
}
if (trans(2, 0)) {
dfs();
trans(2, 1);
}
if (trans(3, 0)) {
dfs();
trans(3, 1);
}
}
int main() {
// freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
v[0] = 1;
dfs();
return 0;
}
```
## 输入样例
```
```
## 输出样例
```
过河
A > 农夫 羊
B > 狼 菜
---------------------
农夫自己过河
A > 羊
B > 农夫 狼 菜
---------------------
带 狼 过河
A > 农夫 狼 羊
B > 菜
---------------------
带 羊 过河
A > 狼
B > 农夫 羊 菜
---------------------
带 菜 过河
A > 农夫 狼 菜
B > 羊
---------------------
农夫自己过河
A > 狼 菜
B > 农夫 羊
---------------------
带 羊 过河
A > 农夫 狼 羊 菜
B >
---------------------
```
# 实验三 简易电子表格的设计
## 问题描述
设计一个支持基本计算统计功能和其它一些表格管理/处理功能的软件,使用
户可在该软件的支持下,用交互方式进行表格建立、数据输入、数据编辑及其它
一些表格操作。即类似于简易Execel表格处理软件。
## 基本要求
1. 建立表格:建立空白表格,同时在屏幕上显示,使其处于可输入数据的状态。用户可指定新建表格的行列数。
2. 输入数据与编辑数据:通过键盘将数据输入到显示在屏幕上的电子表格上。
3. 基本统计计算:可选择按行或列合计、求平均、求最大最小。
4. 排序:使任意指定的行或列中的数据按大小(升或降)排列,对字符型数据,
还可选择大小写敏感。
5. 表格保存:使电子表格以磁盘文件的方式存储在磁盘上,并可随时读入,
供继续处理。
6. 公式支持*:单元格内可输入公式(表达式),使对应单元格的最终内容为公
式的计算结果。公式最基本的形式是算术计算公式,可按名引用其它单元格。
*该功能可选做。
## 思路
参考代码中的 `main.c`,使用 `TableInfo` 结构体存储表格信息,使用 `key.h` 存储键盘按键的宏定义,使用 `conio.h` 实现光标移动,使用 `stdlib.h` 实现 `malloc` 函数,使用 `string.h` 实现 `strcpy` 函数,使用 `stdio.h` 实现 `printf` 函数,使用 `math.h` 实现 `atoi` 函数。
## 代码及解释
`key.h`
```c++
#define KEY_LEFT_ARROW 0x4B // 左箭头
#define KEY_UP_ARROW 0x48 // 上箭头
#define KEY_RIGHT_ARROW 0x4D // 右箭头
#define KEY_DOWN_ARROW 0x50 // 下箭头
#define KEY_ENTER 0x0D // 回车
#define KEY_ESC 0x1B // ESC
```
`main.c`
```c++
#include "key.h"
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 一些常量
#define MAX_COLUMN 20
#define MAX_ROW 20
#define MAX_COLUMN_WIDTH 10
#define MAX_DATA_LENGTH 100
#define MAX_TICK 200
#define MAX_MESSAGE_LENGTH 100
#define FUNCTION_NUM 8
// tick 已废弃
int tick = 0;
// 当前光标所在位置
int current_x = 0;
int current_y = 0;
// 当前是否关闭
int alive = 1;
// 选择模式是否启用
int select_mode = 0;
// 从哪里进入选择模式
int select_entry = 0;
// 当前的输入模式
int input_mode = 0;
// 底部的功能列表
char functions[FUNCTION_NUM][100] = {"Save", "Load", "Sum", "Average",
"Max", "Min", "Sort", "Exit"};
// 当前的消息
char *message = "Hi~";
// 表格信息
typedef struct {
int current_width;
int current_height;
int column_width[MAX_COLUMN];
char data[MAX_ROW][MAX_COLUMN][MAX_DATA_LENGTH];
} TableInfo;
// 常用函数
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a < b ? a : b; }
// 计算一个 int 的长度
int int_length(int num) {
if (num == 0)
return 1;
int length = 0;
while (num) {
num /= 10;
length++;
}
return length;
}
// 判断一个字符串是否全是数字
int is_string_all_number(char *str) {
int length = strlen(str);
for (int i = 0; i < length; i++) {
if (str[i] < '0' || str[i] > '9') {
return 0;
}
}
return 1;
}
// 判断一行或一列是否全是数字
int is_index_all_number(TableInfo *table, int type, int index) {
if (type == 0) {
// 行
index = index - 1;
for (int i = 0; i < table->current_width; i++) {
if (strlen(table->data[index][i]) > 0) {
if (!is_string_all_number(table->data[index][i])) {
return 0;
}
}
}
} else {
// 列
index = index - 'A';
for (int i = 0; i < table->current_height; i++) {
if (strlen(table->data[i][index]) > 0) {
if (!is_string_all_number(table->data[i][index])) {
return 0;
}
}
}
}
return 1;
}
// 交换两列
void changeTwoColumn(TableInfo *table, int column1, int column2) {
// 验证两列是否合法且不等
if (column1 == column2 || column1 < 0 || column2 < 0 ||
column1 >= table->current_width || column2 >= table->current_width) {
return;
}
char tmp[MAX_DATA_LENGTH];
for (int i = 0; i < table->current_height; i++) {
strcpy(tmp, table->data[i][column1]);
strcpy(table->data[i][column1], table->data[i][column2]);
strcpy(table->data[i][column2], tmp);
}
}
// 交换两行
void changeTwoRow(TableInfo *table, int row1, int row2) {
// 验证两行是否合法且不等
if (row1 == row2 || row1 < 0 || row2 < 0 || row1 >= table->current_height ||
row2 >= table->current_height) {
return;
}
char tmp[MAX_DATA_LENGTH];
for (int i = 0; i < table->current_width; i++) {
strcpy(tmp, table->data[row1][i]);
strcpy(table->data[row1][i], table->data[row2][i]);
strcpy(table->data[row2][i], tmp);
}
}
// 自定义支持大小写敏感的字符串比较函数
int str_cmp(char *str1, char *str2, int case_sensitive) {
if (case_sensitive) {
return strcmp(str1, str2);
} else {
char *tmp_str1 = (char *)malloc(sizeof(char) * strlen(str1));
char *tmp_str2 = (char *)malloc(sizeof(char) * strlen(str2));
strcpy(tmp_str1, str1);
strcpy(tmp_str2, str2);
for (int i = 0; i < strlen(tmp_str1); i++) {
if (tmp_str1[i] >= 'A' && tmp_str1[i] <= 'Z') {
tmp_str1[i] += 32;
}
}
for (int i = 0; i < strlen(tmp_str2); i++) {
if (tmp_str2[i] >= 'A' && tmp_str2[i] <= 'Z') {
tmp_str2[i] += 32;
}
}
int result = strcmp(tmp_str1, tmp_str2);
free(tmp_str1);
free(tmp_str2);
return result;
}
}
// 判断一个 index 是否合法
int check_index_valid(TableInfo *table, char *index) {
// 合法的 index 应该是 A1,A23 这样的格式
int length = strlen(index);
if (length == 0) {
return 0;
}
if (index[0] >= 'A' && index[0] <= 'A' + table->current_width - 1) {
// 列号合法
int row = 0;
for (int i = 1; i < length; i++) {
if (index[i] < '0' || index[i] > '9') {
return 0;
}
row = row * 10 + index[i] - '0';
}
if (row >= 1 && row <= table->current_height) {
return 1;
}
}
return 0;
}
// 传入实际的 index
int check_valid_formatter(TableInfo *table, int x, int y) {
// 合法的公示应该以 = 开头,并且只包含 + - 和
char *data = malloc(sizeof(char) * MAX_DATA_LENGTH);
strcpy(data, table->data[y][x]);
if (data[0] != '=') {
return 0;
}
int length = strlen(data);
char *tmp_data = malloc(sizeof(char) * MAX_DATA_LENGTH);
for (int i = 1; i < length; i++) {
if (data[i] == ' ')
continue;
// 获取 =,-,+,\n 之间的字符串
int j = i;
while (j < length && data[j] != '=' && data[j] != '-' &&
data[j] != '+' && data[j] != '\n') {
tmp_data[j - i] = data[j];
j++;
}
tmp_data[j - i] = '\0';
// 判断是否是合法的 index
if (tmp_data[0] >= 'A' && tmp_data[0] <= 'Z') {
if (!check_index_valid(table, tmp_data))
return 0;
} else {
for (int k = 0; k < j - i; k++) {
if (tmp_data[k] < '0' || tmp_data[k] > '9') {
return 0;
}
}
}
}
return 1;
}
// 获取是行还是列
int get_index_type(int index) { return index >> 7; }
// 获取具体的值
int get_index_value(int index) { return index & 0b01111111; }
// 进入选择模式,选择某行或某列
int start_select_index(int entry) {
message = "Select a row or column";
current_x = 0;
current_y = 1;
select_mode = 1;
select_entry = entry;
}
// 退出选择模式
void end_select_index(TableInfo *table) {
// 一个 8 位数字
// 最高位: 0 代表行 1 代表列
// 低 7 位: 代表行号或列号对应字符的 ASCII 码
int select_value = 0;
if (current_x == 0) {
// x = 0 表示选择行
select_value = 0b00000000 | current_y;
} else {
// x > 0 表示选择列
select_value = 0b10000000 | (current_x + 'A' - 1);
}
int type = get_index_type(select_value);
int value = get_index_value(select_value);
int ascii_sort = 0;
// 0 代表不区分大小写1 代表区分大小写
int case_sensitive = 0;
// 判断是否和数字操作有关
if (select_entry == 2 || select_entry == 3 || select_entry == 4 ||
select_entry == 5 || select_entry == 6) {
int valid = is_index_all_number(table, type, value);
if (!valid) {
if (select_entry == 6) {
ascii_sort = 1;
message =
"This row or column is not all number, use ASCII to "
"sort!\n Do you want to use Case Sensitive? ([Y]/n): ";
// 等待完善:选择大小写敏感
// char tmp;
// input_mode = 1;
// scanf("%c", &tmp);
// input_mode = 0;
// if (tmp == 'n' || tmp == 'N') {
// case_sensitive = 0;
// } else {
// case_sensitive = 1;
// }
} else {
message = "This row or column is not all number!";
return;
}
}
}
if (select_entry == 2) {
// sum
int sum = 0;
if (type == 0) {
// 行
value = value - 1;
for (int i = 0; i < table->current_width; i++) {
if (strlen(table->data[value][i]) > 0) {
sum += atoi(table->data[value][i]);
}
}
} else {
// 列
value = value - 'A';
for (int i = 0; i < table->current_height; i++) {
if (strlen(table->data[i][value]) > 0) {
sum += atoi(table->data[i][value]);
}
}
}
char *tmp_message = (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH);
sprintf(tmp_message, "Sum: %d", sum);
message = tmp_message;
} else if (select_entry == 3) {
// average
int sum = 0;
int count = 0;
if (type == 0) {
// 行
value = value - 1;
for (int i = 0; i < table->current_width; i++) {
if (strlen(table->data[value][i]) > 0) {
sum += atoi(table->data[value][i]);
count++;
}
}
} else {
// 列
value = value - 'A';
for (int i = 0; i < table->current_height; i++) {
if (strlen(table->data[i][value]) > 0) {
sum += atoi(table->data[i][value]);
count++;
}
}
}
char *tmp_message = (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH);
sprintf(tmp_message, "Average: %f", (double)sum / count);
message = tmp_message;
} else if (select_entry == 4) {
// max
int max_ = -0x7fffffff;
if (type == 0) {
// 行
value = value - 1;
for (int i = 0; i < table->current_width; i++) {
if (strlen(table->data[value][i]) > 0) {
max_ = atoi(table->data[value][i]) > max_
? atoi(table->data[value][i])
: max_;
}
}
} else {
// 列
value = value - 'A';
for (int i = 0; i < table->current_height; i++) {
if (strlen(table->data[i][value]) > 0) {
max_ = atoi(table->data[i][value]) > max_
? atoi(table->data[i][value])
: max_;
}
}
}
char *tmp_message = (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH);
sprintf(tmp_message, "Max: %d", max_);
message = tmp_message;
} else if (select_entry == 5) {
// min
int min_ = 0x7fffffff;
if (type == 0) {
// 行
value = value - 1;
for (int i = 0; i < table->current_width; i++) {
if (strlen(table->data[value][i]) > 0) {
min_ = atoi(table->data[value][i]) < min_
? atoi(table->data[value][i])
: min_;
}
}
} else {
// 列
value = value - 'A';
for (int i = 0; i < table->current_height; i++) {
if (strlen(table->data[i][value]) > 0) {
min_ = atoi(table->data[i][value]) < min_
? atoi(table->data[i][value])
: min_;
}
}
}
char *tmp_message = (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH);
sprintf(tmp_message, "Min: %d", min_);
message = tmp_message;
} else if (select_entry == 6) {
// sort
if (type == 0) {
// 行
value = value - 1;
for (int i = 0; i < table->current_width; i++) {
for (int j = i + 1; j < table->current_width; j++) {
if (!ascii_sort) {
if (atoi(table->data[value][i]) >
atoi(table->data[value][j])) {
changeTwoColumn(table, i, j);
}
message = "Sorted by number!";
} else {
if (str_cmp(table->data[value][i],
table->data[value][j],
case_sensitive) > 0) {
changeTwoColumn(table, i, j);
}
message = "Sorted by ASCII!";
}
}
}
} else {
// 列
value = value - 'A';
for (int i = 0; i < table->current_height; i++) {
for (int j = i + 1; j < table->current_height; j++) {
if (!ascii_sort) {
if (atoi(table->data[i][value]) >
atoi(table->data[j][value])) {
changeTwoRow(table, i, j);
}
message = "Sorted by number!";
} else {
if (str_cmp(table->data[i][value],
table->data[j][value],
case_sensitive) > 0) {
changeTwoRow(table, i, j);
}
message = "Sorted by ASCII!";
}
}
}
}
} else {
// 选择行或列
if (type == 0) {
// 行
value = value - 1;
char *tmp_message =
(char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH);
sprintf(tmp_message, "Selected row: %d", value + 1);
message = tmp_message;
} else {
// 列
value = value - 'A';
char *tmp_message =
(char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH);
sprintf(tmp_message, "Selected column: %c", value + 'A');
message = tmp_message;
}
}
// char *tmp_message = (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH);
// sprintf(tmp_message, "Selected: %d,%c", get_index_type(select_value),
// get_index_value(select_value));
// message = tmp_message;
select_mode = 0;
current_y = -1;
current_x = select_entry;
}
// 将 int 转换为字符串
char *int_to_string(int num) {
char *str = (char *)malloc(sizeof(char) * 100);
sprintf(str, "%d", num);
return str;
}
// 初始化表格
void init_table(int width, int height, int column_width, TableInfo *table) {
table->current_width = width;
table->current_height = height;
for (int i = 0; i < width; i++) {
table->column_width[i] = column_width;
}
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
table->data[i][j][0] = '\0';
}
}
}
// 计算公式
int cal_formula(TableInfo *table, int x, int y) {
char *data = malloc(sizeof(char) * MAX_DATA_LENGTH);
strcpy(data, table->data[y][x]);
int length = strlen(data);
char *tmp_data = malloc(sizeof(char) * MAX_DATA_LENGTH);
int result = 0;
for (int i = 1; i < length; i++) {
if (data[i] == ' ')
continue;
// 标注 -
int minus = 0;
// 获取 =,-,+,\n 之间的字符串
int j = i;
while (j < length && data[j] != '=' && data[j] != '-' &&
data[j] != '+' && data[j] != '\n') {
tmp_data[j - i] = data[j];
j++;
}
tmp_data[j - i] = '\0';
// 判断是否是 -
if (data[i - 1] == '-') {
minus = 1;
}
// 判断是否是合法的 index
if (tmp_data[0] >= 'A' && tmp_data[0] <= 'Z') {
if (!check_index_valid(table, tmp_data))
return -1;
// 0 表示 列,剩下的是行
int col = tmp_data[0] - 'A';
int row = 0;
for (int k = 1; k < strlen(tmp_data); k++) {
row = row * 10 + tmp_data[k] - '0';
}
// 判断 table->data[row - 1][col] 是否是数字
if (strlen(table->data[row - 1][col]) == 0) {
return -1;
}
if (!is_string_all_number(table->data[row - 1][col])) {
return -1;
}
if (minus) {
result -= atoi(table->data[row - 1][col]);
} else {
result += atoi(table->data[row - 1][col]);
}
} else {
for (int k = 0; k < j - i; k++) {
if (tmp_data[k] < '0' || tmp_data[k] > '9') {
return -1;
}
}
if (minus) {
result -= atoi(tmp_data);
} else {
result += atoi(tmp_data);
}
}
i += j - i;
}
return result;
}
// 输出表格
void printTable(TableInfo *table) {
system("cls");
printf("Table v0.2 by Luthics\n");
int max_l = int_length(table->current_height) + 1;
// render table
for (int i = -1; i < table->current_height; i++) {
for (int j = -1; j < table->current_width; j++) {
printf("+");
if (j == -1) {
for (int k = 0; k < max_l; k++) {
printf("-");
}
continue;
}
for (int k = 0; k < table->column_width[j]; k++) {
printf("-");
}
}
printf("+\n");
if (i == -1) {
// 输出 A-Z 的列名
for (int j = -1; j < table->current_width; j++) {
if (j == -1) {
printf("|");
for (int k = 0; k < max_l; k++) {
printf(" ");
}
continue;
}
printf("|");
for (int k = 0; k < table->column_width[j] - 1; k++) {
printf(" ");
}
if (select_mode) {
if (current_x - 1 == j && current_y == 0) {
// RED
printf("\033[31m");
// BOLD
printf("\033[1m");
}
}
printf("%c", 'A' + j);
if (select_mode) {
if (current_x - 1 == j && current_y == 0) {
// RESET
printf("\033[0m");
}
}
}
printf("|\n");
continue;
}
for (int j = -1; j < table->current_width; j++) {
if (j == -1) {
printf("|");
int data_length = int_length(i + 1);
for (int k = 0; k < max_l - data_length; k++) {
printf(" ");
}
if (select_mode) {
if (current_x == 0 && current_y == i + 1) {
// RED
printf("\033[31m");
// BOLD
printf("\033[1m");
}
}
printf("%d", i + 1);
if (select_mode) {
if (current_x == 0 && current_y == i + 1) {
// RESET
printf("\033[0m");
}
}
continue;
}
printf("|");
int data_length =
table->data[i][j] == NULL ? 0 : strlen(table->data[i][j]);
if (data_length == 0) {
if (i == current_y && j == current_x && !select_mode) {
printf("_");
} else {
printf(" ");
}
for (int k = 0; k < table->column_width[j] - 1; k++) {
printf(" ");
}
} else {
int formatter = 0;
if (table->data[i][j][0] == '=') {
formatter = 1;
}
int add_ = 0;
if (select_mode) {
if (current_x == j + 1 || current_y == i + 1) {
// RED
printf("\033[31m");
// BOLD
printf("\033[1m");
}
} else if (i == current_y && j == current_x) {
// RED
printf("\033[31m");
// BOLD
printf("\033[1m");
} else if (formatter) {
// BLUE
printf("\033[34m");
// BOLD
printf("\033[1m");
}
int formatter_success = 0;
if (formatter && !select_mode &&
!(i == current_y && j == current_x)) {
// todo 先计算后渲染修复列宽不合适的bug
int data = cal_formula(table, j, i);
if (data == -1) {
formatter_success = 0;
} else {
char *tmp_data =
(char *)malloc(sizeof(char) * MAX_DATA_LENGTH);
sprintf(tmp_data, "%d", data);
data_length = strlen(tmp_data);
printf("%s", tmp_data);
table->column_width[j] =
max(table->column_width[j], strlen(tmp_data));
formatter_success = 1;
}
}
if (!formatter_success) {
for (int k = 0; k < data_length; k++) {
printf("%c", table->data[i][j][k]);
}
}
if (select_mode) {
if (current_x == j + 1 || current_y == i + 1) {
// RESET
printf("\033[0m");
}
} else if (i == current_y && j == current_x) {
// RESET
printf("\033[0m");
// 如果还有空间,就补上下划线
if (table->column_width[j] - data_length > 0) {
printf("_");
add_ = 1;
}
} else if (formatter) {
// Reset
printf("\033[0m");
}
if (formatter) {
// 补上空格
for (int k = 0; k < table->column_width[j] - data_length;
k++) {
printf(" ");
}
} else {
for (int k = 0;
k < table->column_width[j] - data_length - add_; k++) {
printf(" ");
}
}
}
}
printf("|\n");
}
for (int j = -1; j < table->current_width; j++) {
if (j == -1) {
printf("+");
for (int k = 0; k < max_l; k++) {
printf("-");
}
continue;
}
printf("+");
for (int k = 0; k < table->column_width[j]; k++) {
printf("-");
}
}
printf("+\n");
// render functions
for (int i = 0; i < FUNCTION_NUM; i++) {
if (current_y == -1 && i == current_x) {
// RED
printf("\033[31m");
// BOLD
printf("\033[1m");
}
printf("%s", functions[i]);
if (current_y == -1 && i == current_x) {
// RESET
printf("\033[0m");
}
if (i != FUNCTION_NUM - 1) {
printf(" ");
}
}
printf("\n");
printf("%s\n", message);
}
// 判断数据文件是否存在
int is_data_file_exist() {
FILE *fp = fopen("table.dat", "r");
if (fp == NULL) {
return 0;
} else {
fclose(fp);
return 1;
}
}
// 从文件中读取表格信息
TableInfo load_table_from_file(char *filename) {
TableInfo table;
FILE *fp = fopen(filename, "r");
fread(&table, sizeof(TableInfo), 1, fp);
fclose(fp);
return table;
}
// 将表格信息写入文件
int save_table_to_file(char *filename, TableInfo table) {
FILE *fp = fopen(filename, "w");
fwrite(&table, sizeof(TableInfo), 1, fp);
fclose(fp);
return 0;
}
// 设置单元格的值
void setCell(TableInfo *table, int row, int column, char *data) {
if (row >= table->current_height || column >= table->current_width ||
row < 0 || column < 0) {
return;
}
strcpy(table->data[row][column], data);
if (strlen(data) > table->column_width[column]) {
table->column_width[column] = strlen(data);
}
}
// 获取单元格的值
int get_max_width_in_a_column(TableInfo *table, int column) {
int max_width = 0;
for (int i = 0; i < table->current_height; i++) {
int data_length =
table->data[i][column] == NULL ? 0 : strlen(table->data[i][column]);
if (data_length > max_width) {
max_width = data_length;
}
}
return max(max_width, table->column_width[column]);
}
// 向单元格中添加字符
void add_char_to_cell(TableInfo *table, int row, int column, char c) {
if (row >= table->current_height || column >= table->current_width ||
row < 0 || column < 0) {
return;
}
int data_length = strlen(table->data[row][column]);
if (data_length < MAX_DATA_LENGTH - 1) {
table->data[row][column][data_length] = c;
table->data[row][column][data_length + 1] = '\0';
table->column_width[column] = get_max_width_in_a_column(table, column);
}
}
// 从单元格中删除字符
void del_char_from_cell(TableInfo *table, int row, int column) {
if (row >= table->current_height || column >= table->current_width ||
row < 0 || column < 0) {
return;
}
int data_length = strlen(table->data[row][column]);
if (data_length > 0) {
table->data[row][column][data_length - 1] = '\0';
table->column_width[column] = get_max_width_in_a_column(table, column);
}
}
// 返回 1 代表响应成功
// 返回 0 代表响应失败
int handleKeyPress(int key, TableInfo *table) {
int status = 0;
if (key == 224) { // 特殊键
key = getch(); // 获取特殊键码
if (key == KEY_LEFT_ARROW) {
if (select_mode) {
// 选择模式0,0是左上角0,1 0,2 0,3 代表每行行号 1,0 2,0 3,0
// 代表每列列号
if (current_y == 0) {
if (current_x > 1)
current_x--;
else
current_x = table->current_width;
}
if (current_x == 0) {
current_y = 0;
current_x = table->current_width;
}
status = 1;
} else {
if (current_x > 0) {
current_x--;
} else if (current_x == 0) {
if (current_y == -1) {
current_x = FUNCTION_NUM - 1;
} else {
current_x = table->current_width - 1;
}
}
status = 1;
}
}
if (key == KEY_RIGHT_ARROW) {
if (select_mode) {
if (current_y == 0) {
if (current_x < table->current_width)
current_x++;
else
current_x = 1;
}
if (current_x == 0) {
current_y = 0;
current_x = 1;
}
status = 1;
} else {
if (current_y == -1) {
current_x = (current_x + 1) % FUNCTION_NUM;
} else {
current_x = (current_x + 1) % table->current_width;
}
status = 1;
}
}
if (key == KEY_UP_ARROW) {
if (select_mode) {
if (current_x == 0) {
if (current_y > 1)
current_y--;
else
current_y = table->current_height;
}
if (current_y == 0) {
current_x = 0;
current_y = table->current_height;
}
status = 1;
} else {
if (current_y > 0) {
current_y--;
} else if (current_y == 0) {
current_y = -1;
current_x = min(FUNCTION_NUM - 1, current_x);
} else if (current_y == -1) {
current_y = table->current_height - 1;
current_x = min(table->current_width - 1, current_x);
}
status = 1;
}
}
if (key == KEY_DOWN_ARROW) {
if (select_mode) {
if (current_x == 0) {
if (current_y < table->current_height)
current_y++;
else
current_y = 1;
}
if (current_y == 0) {
current_x = 0;
current_y = 1;
}
status = 1;
} else {
if (current_y < table->current_height - 1 && current_y >= 0) {
current_y++;
} else if (current_y == table->current_height - 1) {
// TO FUNCTIONS AREA
current_y = -1;
current_x = min(FUNCTION_NUM - 1, current_x);
} else if (current_y == -1) {
current_y = 0;
current_x = min(table->current_width - 1, current_x);
}
status = 1;
}
}
} else {
// ctrl + c
if (key == 3) {
alive = 0;
message = "Bye!";
status = 1;
}
// ctrl + s
if (key == 19) {
save_table_to_file("table.dat", *table);
message = "Saved!";
status = 1;
}
if (select_mode) {
// ENTER
if (key == KEY_ENTER) {
end_select_index(table);
status = 1;
}
// ESC
if (key == KEY_ESC) {
select_mode = 0;
current_y = -1;
current_x = select_entry;
message = "Canceled!";
status = 1;
}
} else {
// 判断 key 是可打印字符
if (key >= 32 && key <= 126) {
add_char_to_cell(table, current_y, current_x, key);
status = 1;
}
// 退格键
if (key == 8) {
del_char_from_cell(table, current_y, current_x);
status = 1;
}
// ENTER
if (key == KEY_ENTER) {
if (current_y == -1) {
if (current_x == 0) {
save_table_to_file("table.dat", *table);
message = "Saved!";
}
if (current_x == 1) {
*table = load_table_from_file("table.dat");
message = "Loaded!";
}
if (current_x == 2) {
start_select_index(2);
}
if (current_x == 3) {
start_select_index(3);
}
if (current_x == 4) {
start_select_index(4);
}
if (current_x == 5) {
start_select_index(5);
}
if (current_x == 6) {
start_select_index(6);
}
if (current_x == 7) {
alive = 0;
message = "Bye!";
}
} else {
// 如果以 = 开头
if (table->data[current_y][current_x][0] == '=') {
if (!check_valid_formatter(table, current_x,
current_y)) {
message = "Not a valid formatter!";
}
}
}
}
}
}
int debug = 0;
if (debug) {
char *tmp_message = (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH);
sprintf(tmp_message, "x: %d, y: %d", current_x, current_y);
message = tmp_message;
}
return status;
}
int main() {
TableInfo table;
int load_from_file = 0;
// 先判断是否有数据文件
if (is_data_file_exist()) {
char tmp;
// 询问是否从文件中读取
printf("Load from file? ([Y]/n): ");
scanf("%c", &tmp);
if (tmp == 'n' || tmp == 'N') {
load_from_file = 0;
} else {
load_from_file = 1;
}
}
if (load_from_file) {
table = load_table_from_file("table.dat");
} else {
// 创建新表格
printf("Let's create a table!\n");
int width = 0, height = 0;
int column_width = 0;
while (width <= 0 || width > MAX_COLUMN) {
printf("Input width (1-%d): ", MAX_COLUMN);
scanf("%d", &width);
}
while (height <= 0 || height > MAX_ROW) {
printf("Input height (1-%d): ", MAX_ROW);
scanf("%d", &height);
}
while (column_width <= 0 || column_width > MAX_COLUMN_WIDTH) {
printf("Input column width (1-%d): ", MAX_COLUMN_WIDTH);
scanf("%d", &column_width);
}
init_table(width, height, column_width, &table);
}
// 把第一行第一列的单元格设置为 Hello
setCell(&table, 0, 0, "Hello");
// 主循环
while (alive) {
printTable(&table);
// drawGUI(currentSelection);
int keyPressed = getch(); // 获取键盘输入
handleKeyPress(keyPressed, &table);
// tick = (tick + 1) % MAX_TICK;
}
// 询问是否保存
int save_to_file = 0;
char tmp;
printf("Save to file? ([Y]/n): ");
scanf("%c", &tmp);
if (tmp == 'n' || tmp == 'N') {
save_to_file = 0;
} else {
save_to_file = 1;
}
if (save_to_file) {
save_table_to_file("table.dat", table);
}
return 0;
}
```
# 实验四 八皇后问题
## 问题描述
设在初始状态下在国际象棋的棋盘上没有任何棋子这里的棋子指皇后棋子。然后顺序在第1行第2行……第8行上布放棋子。在每一行中共有8个可选择的位置但在任一时刻棋盘的合法布局都必须满足3个限制条件1任意两个棋子不得放在同一行2任意两个棋子不得放在同一列上3任意棋子不得放在同一正斜线和反斜线上。
## 基本要求
编写求解并输出此问题的一个合法布局的程序。
## 实现提示:
在第i行布放棋子时从第1列到第8列逐列考察。当在第i行第j列布放棋子时需要考察布放棋子后在行方向、列方向、正斜线和反斜线方向上的布局状态是否合法若该棋子布放合法再递归求解在第i+1行布放棋子若该棋子布放不合法移去这个棋子恢复布放该棋子前的状态然后再试探在第i行第j+1列布放棋子。
## 思路
使用回溯法,从第一行开始,每一行都有 n 个选择,如果选择合法,就继续下一行,否则就回溯到上一行,重新选择。
## 代码
参考 洛谷 P1219 实现的 `n` 皇后问题
```c
#include <stdio.h>
#include <stdlib.h>
// 每行每列每个对角线是否被占用
int ans, a[10005];
int n;
int cx[100], zx[100], col[100];
void pt() {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (a[i] == j) {
printf("Q");
} else {
printf(".");
}
}
printf("\n");
}
printf("\n");
for (int i = 1; i <= n; i++) {
printf("%d ", a[i]);
}
printf("\n");
}
// 回溯法
void dfs(int x) {
if (x > n) {
ans++;
if (ans <= 3) {
pt();
// for (int i = 1; i <= n; i++) {
// printf("%d ", a[i]);
// }
// printf("\n");
}
return;
}
for (int i = 1; i <= n; i++) {
int l = x + i, r = x - i + 15;
// 如果这一列、这一正对角线、这一反对角线都没有被占用
if (cx[r] == 0 && zx[l] == 0 && col[i] == 0) {
a[x] = i;
cx[r] = 1;
zx[l] = 1;
col[i] = 1;
dfs(x + 1);
cx[r] = 0;
zx[l] = 0;
col[i] = 0;
}
}
}
int main() {
scanf("%d", &n);
dfs(1);
printf("%d", ans);
return 0;
}
// Ref: P1219 [USACO1.5] 八皇后 Checker Challenge
// https://www.luogu.com.cn/record/40132197
```
## 输入
```
8
```
## 输出
```
Q.......
....Q...
.......Q
.....Q..
..Q.....
......Q.
.Q......
...Q....
1 5 8 6 3 7 2 4
Q.......
.....Q..
.......Q
..Q.....
......Q.
...Q....
.Q......
....Q...
1 6 8 3 7 4 2 5
Q.......
......Q.
...Q....
.....Q..
.......Q
.Q......
....Q...
..Q.....
1 7 4 6 8 2 5 3
92
```
## 输入2
```
12
```
## 输出2
```
Q...........
..Q.........
....Q.......
.......Q....
.........Q..
...........Q
.....Q......
..........Q.
.Q..........
......Q.....
........Q...
...Q........
1 3 5 8 10 12 6 11 2 7 9 4
Q...........
..Q.........
....Q.......
.........Q..
.......Q....
..........Q.
.Q..........
...........Q
.....Q......
........Q...
......Q.....
...Q........
1 3 5 10 8 11 2 12 6 9 7 4
Q...........
..Q.........
....Q.......
.........Q..
.......Q....
..........Q.
.Q..........
...........Q
......Q.....
........Q...
...Q........
.....Q......
1 3 5 10 8 11 2 12 7 9 4 6
14200
```
# 实验五 约瑟夫环问题仿真
## 问题描述
设编号为`12n(n>0)`个人按顺时针方向围坐一圈,每人持有一个正整数密码。开始时任意给出一个报数上限`m`从第一个人开始顺时针方向自1起顺序报数报到`m`时停止报数,报`m`的人出列,将他的密码作为新的`m`值,从他在顺时针方向上的下一个人起重新自`1`报数;如此下去直到所有人全部出列为止。
## 基本要求
设计一个程序模拟此过程,给出出列人的编号序列。
## 实现提示:
可考虑不带头结点的单链表结构。
## 测试数据:
`N=7`,七个人的密码依次为`3172484`.
初始报数上限值`m=20`。
## 思路
使用循环链表,每次报数到 m 时,就删除这个节点,然后从下一个节点开始重新报数,直到报到这个节点对应的数字,再次循环。
## 代码
```c
#include <stdio.h>
#include <stdlib.h>
int rs[105]; // 每个人的状态
int n; // 人数
int xcz; // 剩余人数
int m[105], m0; // 间隔
int zj(int a) {
// 寻找下一个活着的人
a++;
if (a > n)
a -= n;
while (rs[a] == 0 && xcz > 0) {
a++;
if (a > n)
a -= n;
}
return a;
}
int main() {
freopen("data.in", "r", stdin);
scanf("%d%d", &n, &m0);
for (int i = 1; i <= n; i++)
scanf("%d", &m[i]);
xcz = n;
for (int i = 1; i <= n; i++)
// 把每个人设置为活着
rs[i] = 1;
while (m0 > n) {
m0 -= n;
}
for (int i = m0; xcz > 0;) {
if (rs[i] == 1) {
rs[i] = 0;
xcz--;
printf("%d ", i);
}
int ls = m[i];
// printf("ls=%d\n", ls);
while (ls--)
i = zj(i);
}
return 0;
}
// Ref: P1996 约瑟夫问题
// 已修改为实验版本
```
## 输入
```
7 20
3 1 7 2 4 8 4
```
## 输出
```
6 1 4 7 2 3 5
```
# 实验六 教学计划编制问题
## 问题描述
大学的每个专业都要制定教学计划。假设任何专业都有固定的学习年
限,每学年含两学期,每学期的时间长度和学分上限值均相等。每个专业
开设的课程都是固定的,而且课程在开设时间的安排必须满足先修关系。
每门课程有哪些先修课程是确定的,可以有任意多门,也可以没有。每门
课恰好占一个学期。试在这样的前提下设计一个教学计划编制程序。
## 基本要求
1. 输入参数包括:学期总数,一学期的学分上限,每门课的课程号
(固定占3位的字母数字串)、学分和直接先修课的课程号。
2. 允许用户指定下列两种编排策略之一:一是使学生在各学期中的
学习负担尽量均匀;二是使课程尽可能地集中在前几个学期中。
3. 若根据给定的条件问题无解,则报告适当的信息;否则,将教学
计划输出到用户指定的文件中。计划的表格格式自行设计。
## 实现提示:
可设学期总数不超过12课程总数小于100。如果输入的先修课程号不在该专业开设的课程序列中则作为错误处理。
## 思路:
使用拓扑排序,先将课程按照先修课程的数量进行排序,先修课程数量越多的课程越靠前,然后从前往后,如果学期学习数量小于平均值,则将前面的课程往后移动。
## 代码:
```c++
#include <bits/stdc++.h>
using namespace std;
int se; // 学期总数
int sx; // 学分上限
int cl; // 课程数量
int ren[12], fe[12], re[12][105];
struct Class {
string id; // 课程编号
int sx; // 学分
int xq;
int sel; // 是否已经选择
int pren, nexn; // 先修课程数量
int ppren; // use for topo
string pre[105]; // 先修课程
string next[105]; // 后续课程
} ss[105];
// 输出具体的课程安排
void pt(int detail = 0) {
// print
for (int i = 0; i < cl; i++) {
cout << ss[i].id << " " << ss[i].sx << " " << ss[i].pren << " "
<< ss[i].nexn << endl;
if (!detail)
continue;
if (ss[i].pren > 0) {
cout << "pre:" << endl;
for (int j = 0; j < ss[i].pren; j++) {
cout << ss[i].pre[j] << " ";
}
cout << endl;
}
if (ss[i].nexn > 0) {
cout << "next:" << endl;
for (int j = 0; j < ss[i].nexn; j++) {
cout << ss[i].next[j] << " ";
}
cout << endl;
}
cout << endl;
}
}
// 寻找 id
int fd(string id) {
for (int i = 0; i < cl; i++) {
if (ss[i].id == id) {
return i;
}
}
return -1;
}
// 去除字符串两边空格和换行符
string ql(string s) {
int l = 0, r = s.length() - 1;
while (s[l] == ' ' || s[l] == '\n')
l++;
while (s[r] == ' ' || s[r] == '\n')
r--;
return s.substr(l, r - l + 1);
}
// 寻找先修课程的最迟学期
int xz(int i) {
// cout << ss[i].id << " " << ss[i].pren << endl;
if (ss[i].pren == 0)
return 0;
int maxn = 0;
for (int j = 0; j < ss[i].pren; j++) {
int fid = fd(ss[i].pre[j]);
if (ss[fid].xq > maxn) {
maxn = ss[fid].xq;
}
}
return maxn + 1;
}
// 寻找后修课程的最早学期
int xz2(int i) {
if (ss[i].nexn == 0)
return se;
int minn = se;
for (int j = 0; j < ss[i].nexn; j++) {
int fid = fd(ss[i].next[j]);
if (ss[fid].xq < minn) {
minn = ss[fid].xq;
}
}
return minn;
}
// 拓扑排序将排好序的id 放入sorted
void topo() {
int sorted[105] = {0};
int cnt = 0;
queue<Class *> q;
for (int i = 0; i < cl; i++) {
if (ss[i].ppren == 0) {
q.push(&ss[i]);
}
}
while (!q.empty()) {
Class *tmp = q.front();
sorted[cnt] = fd(tmp->id);
q.pop();
cnt++;
for (int i = 0; i < tmp->nexn; i++) {
ss[fd(tmp->next[i])].ppren--;
if (ss[fd(tmp->next[i])].ppren == 0) {
q.push(&ss[fd(tmp->next[i])]);
}
}
}
if (cnt != cl) {
cout << "error" << endl;
exit(0);
}
Class newss[105];
// ss 顺序改为 sorted 顺序
for (int i = 0; i < cl; i++) {
newss[i] = ss[sorted[i]];
}
for (int i = 0; i < cl; i++) {
ss[i] = newss[i];
}
}
void avgf() {
// 计算平均每学期学习数量
int avg = cl / se;
// cout << avg << endl;
// 按从后向前如果学期学习数量小于平均值则将前面的课程往后移动
for (int i = se - 1; i >= 1; i--) {
if (ren[i] < avg) {
for (int j = i - 1; j >= 0; j--) {
// cout << j << " " << ren[j] << endl;
if (ren[j] <= 0)
continue;
int flag = 0;
for (int ii = 0; ii < ren[j] && ren[i] < avg; ii++) {
int c = re[j][ii];
// cout << " " << ss[c].id << " " << xz2(2)<< endl;
// cout << c << endl;
if (xz2(c) > i) {
// 把 c 课程放到 i 学期
re[i][ren[i]++] = c;
ss[c].xq = i;
// 把 c 课程从 j 学期删除
for (int jj = ii; jj < ren[j] - 1; jj++) {
re[j][jj] = re[j][jj + 1];
}
ren[j]--;
flag = 1;
}
// 如果 i 学期中的课程数量大于平均值则跳出循环
if (ren[i] >= avg) {
break;
}
if (flag == 1) {
ii = -1;
flag = 0;
}
}
}
}
}
}
// 输出最终结果
void pte() {
for (int i = 0; i < se; i++) {
if (ren[i] == 0)
continue;
cout << "学期 " << i + 1 << " 学分 " << fe[i] << " 课程数 " << ren[i]
<< " 课程";
for (int j = 0; j < ren[i]; j++) {
cout << ss[re[i][j]].id << " ";
}
cout << endl;
}
}
int main() {
freopen("in.txt", "r", stdin);
cin >> se >> sx;
if (se <= 0 || sx <= 0 || se > 6 || sx > 10) {
cout << "error" << endl;
exit(0);
}
cin >> cl;
if (cl <= 0 || cl > 12) {
cout << "error" << endl;
exit(0);
}
for (int i = 0; i < cl; i++) {
cin >> ss[i].id;
ss[i].id = ql(ss[i].id);
}
for (int i = 0; i < cl; i++) {
// 学分和前置课程
cin >> ss[i].sx >> ss[i].pren;
ss[i].ppren = ss[i].pren;
for (int j = 0; j < ss[i].pren; j++) {
string tmp;
cin >> tmp;
tmp = ql(tmp);
if (tmp.size() == 0)
continue;
int fid = fd(tmp);
if (fid == -1) {
cout << "error" << endl;
exit(0);
}
ss[i].pre[j] = ss[fid].id;
ss[fid].next[ss[fid].nexn++] = ss[i].id;
}
}
// pt(1);
topo();
// pt(1);
for (int i = 0; i < cl; i++) {
ss[i].xq = xz(i);
}
int ttc = 0;
for (int i = 0; i < se; i++) {
for (int j = 0; j < cl; j++) {
// 判断是否已选择
if (ss[j].sel == 1) {
continue;
}
// 判断是否学分超过上限
if (ss[j].sx + fe[i] > sx) {
continue;
}
// 判断是否先修课程已经学习
int flag = 0;
for (int k = 0; k < ss[j].pren; k++) {
int fid = fd(ss[j].pre[k]);
if (ss[fid].sel == 0) {
flag = 1;
break;
}
}
if (flag == 1) {
continue;
}
// 选择该课程
ss[j].sel = 1;
fe[i] += ss[j].sx;
re[i][ren[i]++] = j;
ttc++;
}
}
// 判断是否全部选中
if (ttc != cl) {
cout << "error" << endl;
exit(0);
}
// for (int i = 0; i < cl; i++) {
// cout << ss[i].id << " " << ss[i].xq << endl;
// }
// pte();
int kind;
cin >> kind;
if (kind != 1 && kind != 2) {
cout << "error" << endl;
exit(0);
}
// 输入分配方式1是负担均匀2是尽早学习
if (kind == 1) {
avgf();
} else if (kind == 2) {
// 本来就是今早分配的无需更改
} else {
cout << "error" << endl;
exit(0);
}
// 输出最终结果
pte();
return 0;
}
```
## 输入
平均负载
```
6 10
12
C01 C02 C03 C04 C05 C06 C07 C08 C09 C10 C11 C12
2 0
3 1 C01
4 2 C01 C02
3 1 C01
2 2 C03 C04
3 1 C11
4 2 C03 C05
4 2 C03 C06
7 0
5 1 C09
2 1 C09
3 3 C01 C09 C10
1
```
## 输出
```
学期 1 学分 9 课程数 2 课程C01 C09
学期 2 学分 8 课程数 2 课程C02 C11
学期 3 学分 9 课程数 2 课程C03 C04
学期 4 学分 8 课程数 2 课程C05 C10
学期 5 学分 8 课程数 2 课程C12 C06
学期 6 学分 0 课程数 2 课程C08 C07
```
## 输入2
尽早学完
```
6 10
12
C01 C02 C03 C04 C05 C06 C07 C08 C09 C10 C11 C12
2 0
3 1 C01
4 2 C01 C02
3 1 C01
2 2 C03 C04
3 1 C11
4 2 C03 C05
4 2 C03 C06
7 0
5 1 C09
2 1 C09
3 3 C01 C09 C10
2
```
## 输出2
```
学期 1 学分 9 课程数 2 课程C01 C09
学期 2 学分 8 课程数 3 课程C02 C04 C11
学期 3 学分 9 课程数 2 课程C10 C03
学期 4 学分 8 课程数 3 课程C12 C06 C05
学期 5 学分 8 课程数 2 课程C08 C07
```
# 实验七 二叉排序树与平衡二叉树的实现
## 问题描述
分别采用二叉链表和顺序表作存储结构,实现对二叉排序树与平衡二叉树的操作。
## 基本要求
1. 用二叉链表作存储结构实现二叉排序树。
1. 以回车符(\n)为输入结束标志输入数列L生成一棵二叉排序树T
2. 对二叉排序树T作中序遍历输出结果
3. 计算二叉排序树T查找成功的平均查找长度输出结果
4. 输入元素x查找二叉排序树T若存在含x的结点则删除该结点并作中序遍历(执行操作2)否则输出信息“无x”
2. 用顺序表(一维数组)作存储结构----静态链表
1. 以回车符(\n)为输入结束标志输入数列L生成一棵二叉排序树T
2. 对二叉排序树T作中序遍历输出结果
3. 计算二叉排序树T查找成功的平均查找长度输出结果
4. 输入元素x查找二叉排序树T若存在含x的结点则删除该结点并作中序遍历(执行操作2)否则输出信息“无x”
3. 用二叉链表作存储结构实平衡的二叉排序树。
1. 用数列L生成平衡的二叉排序树BT当插入新元素之后发现当前的二叉排序树BT不是平衡的二叉排序树则立即将它转换成新的平衡的二叉排序树BT
2. 计算平衡的二叉排序树BT的平均查找长度输出结果。
## 代码
### 二叉链表
```c
#include <stdio.h>
#include <stdlib.h>
typedef struct Tree {
int val;
struct Tree *l, *r;
struct Tree *parent;
} Tree;
int n;
int m;
void makeTree(Tree *root, int val) {
if (val < root->val) {
if (root->l == NULL) {
root->l = (Tree *)malloc(sizeof(Tree));
root->l->val = val;
root->l->l = NULL;
root->l->r = NULL;
root->l->parent = root;
} else {
makeTree(root->l, val);
}
} else {
if (root->r == NULL) {
root->r = (Tree *)malloc(sizeof(Tree));
root->r->val = val;
root->r->l = NULL;
root->r->r = NULL;
root->r->parent = root;
} else {
makeTree(root->r, val);
}
}
}
// 中序遍历
void inOrder(Tree *root) {
if (root == NULL)
return;
inOrder(root->l);
printf("%d ", root->val);
inOrder(root->r);
}
// 二叉排序树T查找成功的平均查找长度
double ASL(Tree *root, int level) {
if (root == NULL)
return 0;
return level + ASL(root->l, level + 1) + ASL(root->r, level + 1);
}
// 找到二叉树中值为val的节点并删除
void delNode(Tree *root, int val) {
if (root == NULL)
return;
if (root->val == val) {
if (root->l == NULL && root->r == NULL) {
if (root->parent->l == root) {
root->parent->l = NULL;
} else {
root->parent->r = NULL;
}
free(root);
} else if (root->l == NULL) {
if (root->parent->l == root) {
root->parent->l = root->r;
} else {
root->parent->r = root->r;
}
free(root);
} else if (root->r == NULL) {
if (root->parent->l == root) {
root->parent->l = root->l;
} else {
root->parent->r = root->l;
}
free(root);
} else {
Tree *p = root->r;
while (p->l != NULL) {
p = p->l;
}
root->val = p->val;
delNode(p, p->val);
}
} else if (root->val > val) {
delNode(root->l, val);
} else {
delNode(root->r, val);
}
}
void pt(Tree *root) {
printf("inOrder:\n");
inOrder(root);
printf("\n");
printf("ASL: %lf\n", ASL(root, 1) / n);
printf("\n");
}
int main() {
freopen("data.in", "r", stdin);
scanf("%d", &n);
Tree *root = (Tree *)malloc(sizeof(Tree));
root->parent = NULL;
for (int i = 0; i < n; i++) {
int val;
scanf("%d", &val);
if (i == 0) {
root->val = val;
root->l = NULL;
root->r = NULL;
} else {
makeTree(root, val);
}
}
pt(root);
scanf("%d", &m);
for (int i = 0; i < m; i++) {
int x;
scanf("%d", &x);
delNode(root, x);
pt(root);
}
return 0;
}
```
#### 输入
10
2 5 10 8 7 9 4 6 1 3
3
2
5
9
#### 输出
```
inOrder:
1 2 3 4 5 6 7 8 9 10
ASL: 3.500000
inOrder:
1 3 4 5 6 7 8 9 10
ASL: 3.100000
inOrder:
1 3 4 6 7 8 9 10
ASL: 2.500000
inOrder:
1 3 4 6 7 8 10
ASL: 2.000000
```
### 顺序表
```c
#include <stdio.h>
#include <stdlib.h>
int n;
int m;
// 二叉排序树,根节点为 1左子树为 2 * i右子树为 2 * i + 1
int tree[1000];
void makeTree(int val) {
int r = 1;
while (1) {
if (val < tree[r]) {
if (tree[2 * r] == 0) {
// printf("r: %d, val: %d\n", 2 * r, val);
tree[2 * r] = val;
break;
} else {
r = 2 * r;
}
} else {
if (tree[2 * r + 1] == 0) {
// printf("r: %d, val: %d\n", 2 * r + 1, val);
tree[2 * r + 1] = val;
break;
} else {
r = 2 * r + 1;
}
}
}
}
// 中序遍历
void inOrder(int root) {
if (tree[2 * root] != 0)
inOrder(2 * root);
printf("%d ", tree[root]);
if (tree[2 * root + 1] != 0)
inOrder(2 * root + 1);
}
// 二叉排序树T查找成功的平均查找长度
double ASL(int root, int level) {
if (tree[root] == 0)
return 0;
return level + ASL(2 * root, level + 1) + ASL(2 * root + 1, level + 1);
}
// 找到二叉树中值为val的节点并删除
void delNode(int root, int x) {
if (tree[root] == 0)
return;
if (tree[root] == x) {
if (tree[2 * root] == 0 && tree[2 * root + 1] == 0) {
tree[root] = 0;
} else if (tree[2 * root] != 0 && tree[2 * root + 1] == 0) {
tree[root] = tree[2 * root];
tree[2 * root] = 0;
} else if (tree[2 * root] == 0 && tree[2 * root + 1] != 0) {
tree[root] = tree[2 * root + 1];
tree[2 * root + 1] = 0;
} else {
int r = 2 * root + 1;
while (tree[2 * r] != 0) {
r = 2 * r;
}
tree[root] = tree[r];
tree[r] = 0;
}
} else if (tree[root] > x) {
delNode(2 * root, x);
} else {
delNode(2 * root + 1, x);
}
}
void pt() {
printf("inOrder:\n");
inOrder(1);
printf("\n");
printf("ASL: %lf\n", ASL(1, 1) / n);
printf("\n");
}
int main() {
freopen("data.in", "r", stdin);
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
int val;
scanf("%d", &val);
if (i == 1) {
tree[1] = val;
} else {
makeTree(val);
}
}
pt();
scanf("%d", &m);
for (int i = 0; i < m; i++) {
int x;
scanf("%d", &x);
delNode(1, x);
pt();
}
return 0;
}
```
#### 输入
10
2 5 10 8 7 9 4 6 1 3
3
2
5
9
#### 输出
```
inOrder:
1 2 3 4 5 6 7 8 9 10
ASL: 3.500000
inOrder:
1 3 4 5 6 7 8 9 10
ASL: 3.100000
inOrder:
1 3 4 6 7 8 9 10
ASL: 2.500000
inOrder:
1 3 4 6 7 8 10
ASL: 2.000000
```
### 平衡二叉树
```c
#include <stdio.h>
#include <stdlib.h>
typedef struct Tree {
int val;
struct Tree *l, *r;
struct Tree *parent;
int height;
int balance;
} Tree;
int n;
int m;
// 获取节点高度
int getHeight(Tree *root) {
if (root == NULL)
return 0;
return root->height;
}
// 更新节点高度和平衡因子
void updateHeight(Tree *root) {
if (root == NULL)
return;
int l = getHeight(root->l);
int r = getHeight(root->r);
root->height = (l > r ? l : r) + 1;
root->balance = l - r;
}
// 左旋操作
Tree *leftRotate(Tree *root) {
Tree *newRoot = root->r;
root->r = newRoot->l;
if (newRoot->l != NULL)
newRoot->l->parent = root;
newRoot->l = root;
newRoot->parent = root->parent;
root->parent = newRoot;
updateHeight(root);
updateHeight(newRoot);
return newRoot;
}
// 右旋操作
Tree *rightRotate(Tree *root) {
Tree *newRoot = root->l;
root->l = newRoot->r;
if (newRoot->r != NULL)
newRoot->r->parent = root;
newRoot->r = root;
newRoot->parent = root->parent;
root->parent = newRoot;
updateHeight(root);
updateHeight(newRoot);
return newRoot;
}
// 左右旋操作
Tree *leftRightRotate(Tree *root) {
root->l = leftRotate(root->l);
return rightRotate(root);
}
// 右左旋操作
Tree *rightLeftRotate(Tree *root) {
root->r = rightRotate(root->r);
return leftRotate(root);
}
// 插入节点
Tree *insertNode(Tree *root, int val) {
if (root == NULL) {
Tree *tmp = (Tree *)malloc(sizeof(Tree));
tmp->val = val;
tmp->l = NULL;
tmp->r = NULL;
tmp->parent = NULL;
tmp->height = 1;
tmp->balance = 0;
return tmp;
}
if (val < root->val) {
root->l = insertNode(root->l, val);
root->l->parent = root;
} else {
root->r = insertNode(root->r, val);
root->r->parent = root;
}
updateHeight(root);
if (root->balance > 1) {
if (val < root->l->val) {
return rightRotate(root);
} else {
return leftRightRotate(root);
}
} else if (root->balance < -1) {
if (val > root->r->val) {
return leftRotate(root);
} else {
return rightLeftRotate(root);
}
}
return root;
}
// 删除节点
Tree *deleteNode(Tree *root, int val) {
if (root == NULL)
return NULL;
if (val < root->val) {
root->l = deleteNode(root->l, val);
} else if (val > root->val) {
root->r = deleteNode(root->r, val);
} else {
if (root->l == NULL && root->r == NULL) {
free(root);
return NULL;
} else if (root->l == NULL) {
Tree *temp = root->r;
free(root);
return temp;
} else if (root->r == NULL) {
Tree *temp = root->l;
free(root);
return temp;
} else {
Tree *minNode = root->r;
while (minNode->l != NULL)
minNode = minNode->l;
root->val = minNode->val;
root->r = deleteNode(root->r, minNode->val);
}
}
updateHeight(root);
if (root->balance > 1) {
if (getHeight(root->l->l) >= getHeight(root->l->r)) {
return rightRotate(root);
} else {
return leftRightRotate(root);
}
} else if (root->balance < -1) {
if (getHeight(root->r->r) >= getHeight(root->r->l)) {
return leftRotate(root);
} else {
return rightLeftRotate(root);
}
}
return root;
}
void makeTree(Tree *root, int val) {
if (val < root->val) {
if (root->l == NULL) {
root->l = (Tree *)malloc(sizeof(Tree));
root->l->val = val;
root->l->l = NULL;
root->l->r = NULL;
root->l->parent = root;
root->l->height = root->height + 1;
} else {
makeTree(root->l, val);
}
} else {
if (root->r == NULL) {
root->r = (Tree *)malloc(sizeof(Tree));
root->r->val = val;
root->r->l = NULL;
root->r->r = NULL;
root->r->parent = root;
root->r->height = root->height + 1;
} else {
makeTree(root->r, val);
}
}
}
// 中序遍历
void inOrder(Tree *root) {
if (root == NULL)
return;
inOrder(root->l);
printf("%d ", root->val);
inOrder(root->r);
}
// 二叉排序树T查找成功的平均查找长度
double ASL(Tree *root, int level) {
if (root == NULL)
return 0;
return level + ASL(root->l, level + 1) + ASL(root->r, level + 1);
}
// 找到二叉树中值为val的节点并删除
void delNode(Tree *root, int val) {
if (root == NULL)
return;
if (root->val == val) {
if (root->l == NULL && root->r == NULL) {
if (root->parent->l == root) {
root->parent->l = NULL;
} else {
root->parent->r = NULL;
}
free(root);
} else if (root->l == NULL) {
if (root->parent->l == root) {
root->parent->l = root->r;
} else {
root->parent->r = root->r;
}
free(root);
} else if (root->r == NULL) {
if (root->parent->l == root) {
root->parent->l = root->l;
} else {
root->parent->r = root->l;
}
free(root);
} else {
Tree *p = root->r;
while (p->l != NULL) {
p = p->l;
}
root->val = p->val;
delNode(p, p->val);
}
} else if (root->val > val) {
delNode(root->l, val);
} else {
delNode(root->r, val);
}
}
void detail(Tree *root) {
if (root == NULL)
return;
printf("val: %d, height: %d, balance: %d, parent: %d\n", root->val,
root->height, root->balance,
root->parent == NULL ? -1 : root->parent->val);
detail(root->l);
detail(root->r);
}
void pt(Tree *root) {
printf("inOrder:\n");
inOrder(root);
printf("\n");
detail(root);
printf("ASL: %lf\n", ASL(root, 1) / n);
printf("\n");
}
// 打印二叉树
void printTree(Tree *root, int level) {
if (root == NULL)
return;
printTree(root->r, level + 1);
for (int i = 0; i < level; i++)
printf(" ");
printf("%d\n", root->val);
printTree(root->l, level + 1);
}
int main() {
freopen("data.in", "r", stdin);
scanf("%d", &n);
Tree *root = NULL;
for (int i = 0; i < n; i++) {
int val;
scanf("%d", &val);
root = insertNode(root, val);
}
pt(root);
scanf("%d", &m);
for (int i = 0; i < m; i++) {
int x;
scanf("%d", &x);
root = deleteNode(root, x);
pt(root);
printTree(root, 0);
printf("----------------\n");
}
return 0;
}
```
#### 输入
10
2 5 10 8 7 9 4 6 1 3
3
2
5
9
#### 输出
二叉树图形输出为竖着
```
1 2 3 4 5 6 7 8 9 10
val: 5, height: 4, balance: 0, parent: -1
val: 2, height: 3, balance: -1, parent: 5
val: 1, height: 1, balance: 0, parent: 2
val: 4, height: 2, balance: 1, parent: 2
val: 3, height: 1, balance: 0, parent: 4
val: 8, height: 3, balance: 0, parent: 5
val: 7, height: 2, balance: 1, parent: 8
val: 6, height: 1, balance: 0, parent: 7
val: 10, height: 2, balance: 1, parent: 8
val: 9, height: 1, balance: 0, parent: 10
ASL: 2.900000
inOrder:
1 3 4 5 6 7 8 9 10
val: 5, height: 4, balance: -1, parent: -1
val: 3, height: 2, balance: 0, parent: 5
val: 1, height: 1, balance: 0, parent: 3
val: 4, height: 1, balance: 0, parent: 3
val: 8, height: 3, balance: 0, parent: 5
val: 7, height: 2, balance: 1, parent: 8
val: 6, height: 1, balance: 0, parent: 7
val: 10, height: 2, balance: 1, parent: 8
val: 9, height: 1, balance: 0, parent: 10
ASL: 2.500000
10
9
8
7
6
5
4
3
1
----------------
inOrder:
1 3 4 6 7 8 9 10
val: 6, height: 4, balance: -1, parent: -1
val: 3, height: 2, balance: 0, parent: 6
val: 1, height: 1, balance: 0, parent: 3
val: 4, height: 1, balance: 0, parent: 3
val: 8, height: 3, balance: -1, parent: 6
val: 7, height: 1, balance: 0, parent: 8
val: 10, height: 2, balance: 1, parent: 8
val: 9, height: 1, balance: 0, parent: 10
ASL: 2.100000
10
9
8
7
6
4
3
1
----------------
inOrder:
1 3 4 6 7 8 10
val: 6, height: 3, balance: 0, parent: -1
val: 3, height: 2, balance: 0, parent: 6
val: 1, height: 1, balance: 0, parent: 3
val: 4, height: 1, balance: 0, parent: 3
val: 8, height: 2, balance: 0, parent: 6
val: 7, height: 1, balance: 0, parent: 8
val: 10, height: 1, balance: 0, parent: 8
ASL: 1.700000
10
8
7
6
4
3
1
----------------
```
# 实验九 学生成绩分析
## 问题描述
录入保存一个班级学生多门课程的成绩并对成绩进行分析
## 基本要求
1. 通过键盘输入各学生的多门课程的成绩建立相应的文件input.dat
2. 对文件 input.dat 中的数据进行处理要求具有如下功能
1. 按各门课程成绩排序并生成相应的文件输出
2. 计算每人的平均成绩按平均成绩排序并生成文件
3. 求出各门课程的平均成绩最高分最低分不及格人数60-69分人数70-79分人数 80-89分人数90分以上人数
4. 根据姓名或学号查询某人的各门成绩重名情况也能处理
## 代码
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char *uid;
char *name;
int math, eng, cs;
} Score;
// 保存到文件
void save_to_file(Score **scores, int n, char *file_name) {
FILE *fp = fopen(file_name, "w");
fprintf(fp, "%d\n", n);
for (int i = 0; i < n; i++) {
fprintf(fp, "%s %s %d %d %d\n", scores[i]->uid, scores[i]->name,
scores[i]->math, scores[i]->eng, scores[i]->cs);
}
fclose(fp);
}
int main() {
Score *scores[105];
int n;
// 如果 score.dat 不存在,就从键盘输入
FILE *fp = fopen("score.dat", "r");
if (fp == NULL) {
printf("score.dat not found, please input:\n");
scanf("%d", &n);
fp = fopen("score.dat", "w");
fprintf(fp, "%d\n", n);
for (int i = 0; i < n; i++) {
scores[i] = (Score *)malloc(sizeof(Score));
char *uid = (char *)malloc(sizeof(char) * 105);
char *name = (char *)malloc(sizeof(char) * 105);
scanf("%s %s %d %d %d", uid, name, &scores[i]->math,
&scores[i]->eng, &scores[i]->cs);
scores[i]->uid = uid;
scores[i]->name = name;
fprintf(fp, "%s %s %d %d %d\n", scores[i]->uid, scores[i]->name,
scores[i]->math, scores[i]->eng, scores[i]->cs);
}
fclose(fp);
} else {
// 从 score.dat 读取
fscanf(fp, "%d", &n);
for (int i = 0; i < n; i++) {
scores[i] = (Score *)malloc(sizeof(Score));
char *uid = (char *)malloc(sizeof(char) * 105);
char *name = (char *)malloc(sizeof(char) * 105);
fscanf(fp, "%s %s %d %d %d", uid, name, &scores[i]->math,
&scores[i]->eng, &scores[i]->cs);
scores[i]->uid = uid;
scores[i]->name = name;
}
fclose(fp);
}
printf("Total: %d\n", n);
printf("当前数据:\n");
// 输出所有人的信息
for (int i = 0; i < n; i++) {
printf("%s %s %d %d %d\n", scores[i]->uid, scores[i]->name,
scores[i]->math, scores[i]->eng, scores[i]->cs);
}
printf("--------------------\n");
// 按照每个科目排序并输出对应的排名
printf("按照数学成绩排序:\n");
for (int i = 0; i < n; i++) {
int max = i;
for (int j = i + 1; j < n; j++) {
if (scores[j]->math > scores[max]->math) {
max = j;
}
}
Score *tmp = scores[i];
scores[i] = scores[max];
scores[max] = tmp;
printf("%s %s %d %d %d\n", scores[i]->uid, scores[i]->name,
scores[i]->math, scores[i]->eng, scores[i]->cs);
}
save_to_file(scores, n, "math.dat");
printf("--------------------\n");
printf("按照英语成绩排序:\n");
for (int i = 0; i < n; i++) {
int max = i;
for (int j = i + 1; j < n; j++) {
if (scores[j]->eng > scores[max]->eng) {
max = j;
}
}
Score *tmp = scores[i];
scores[i] = scores[max];
scores[max] = tmp;
printf("%s %s %d %d %d\n", scores[i]->uid, scores[i]->name,
scores[i]->math, scores[i]->eng, scores[i]->cs);
}
save_to_file(scores, n, "eng.dat");
printf("--------------------\n");
printf("按照计算机成绩排序:\n");
for (int i = 0; i < n; i++) {
int max = i;
for (int j = i + 1; j < n; j++) {
if (scores[j]->cs > scores[max]->cs) {
max = j;
}
}
Score *tmp = scores[i];
scores[i] = scores[max];
scores[max] = tmp;
printf("%s %s %d %d %d\n", scores[i]->uid, scores[i]->name,
scores[i]->math, scores[i]->eng, scores[i]->cs);
}
save_to_file(scores, n, "cs.dat");
printf("--------------------\n");
// 计算平均分,按平均成绩排序,写到 average.dat
printf("平均分:\n");
FILE *fp_average = fopen("average.dat", "w");
fprintf(fp_average, "%d\n", n);
double avg_scores[105];
for (int i = 0; i < n; i++) {
avg_scores[i] =
(scores[i]->math + scores[i]->eng + scores[i]->cs) / 3.0;
}
// sort
for (int i = 0; i < n; i++) {
int max = i;
for (int j = i + 1; j < n; j++) {
if (avg_scores[j] > avg_scores[max]) {
max = j;
}
}
int tmp = avg_scores[i];
avg_scores[i] = avg_scores[max];
avg_scores[max] = tmp;
Score *tmp_score = scores[i];
scores[i] = scores[max];
scores[max] = tmp_score;
}
for (int i = 0; i < n; i++) {
printf("%s %s %d %d %d %.2f\n", scores[i]->uid, scores[i]->name,
scores[i]->math, scores[i]->eng, scores[i]->cs,
avg_scores[i] * 1.0);
fprintf(fp_average, "%s %s %d %d %d %.2f\n", scores[i]->uid,
scores[i]->name, scores[i]->math, scores[i]->eng, scores[i]->cs,
avg_scores[i] * 1.0);
}
fclose(fp_average);
printf("--------------------\n");
// 求出各门课程的平均成绩、最高分、最低分、不及格人数、60-69分人数、70-79分人数、
// 80-89分人数、90分以上人数。
int math_sum = 0, eng_sum = 0, cs_sum = 0;
int math_max = 0, eng_max = 0, cs_max = 0;
int math_min = 100, eng_min = 100, cs_min = 100;
int math_fail = 0, eng_fail = 0, cs_fail = 0;
int math_60_69 = 0, eng_60_69 = 0, cs_60_69 = 0;
int math_70_79 = 0, eng_70_79 = 0, cs_70_79 = 0;
int math_80_89 = 0, eng_80_89 = 0, cs_80_89 = 0;
int math_90 = 0, eng_90 = 0, cs_90 = 0;
for (int i = 0; i < n; i++) {
math_sum += scores[i]->math;
eng_sum += scores[i]->eng;
cs_sum += scores[i]->cs;
if (scores[i]->math > math_max) {
math_max = scores[i]->math;
}
if (scores[i]->eng > eng_max) {
eng_max = scores[i]->eng;
}
if (scores[i]->cs > cs_max) {
cs_max = scores[i]->cs;
}
if (scores[i]->math < math_min) {
math_min = scores[i]->math;
}
if (scores[i]->eng < eng_min) {
eng_min = scores[i]->eng;
}
if (scores[i]->cs < cs_min) {
cs_min = scores[i]->cs;
}
if (scores[i]->math < 60) {
math_fail++;
}
if (scores[i]->eng < 60) {
eng_fail++;
}
if (scores[i]->cs < 60) {
cs_fail++;
}
if (scores[i]->math >= 60 && scores[i]->math <= 69) {
math_60_69++;
}
if (scores[i]->eng >= 60 && scores[i]->eng <= 69) {
eng_60_69++;
}
if (scores[i]->cs >= 60 && scores[i]->cs <= 69) {
cs_60_69++;
}
if (scores[i]->math >= 70 && scores[i]->math <= 79) {
math_70_79++;
}
if (scores[i]->eng >= 70 && scores[i]->eng <= 79) {
eng_70_79++;
}
if (scores[i]->cs >= 70 && scores[i]->cs <= 79) {
cs_70_79++;
}
if (scores[i]->math >= 80 && scores[i]->math <= 89) {
math_80_89++;
}
if (scores[i]->eng >= 80 && scores[i]->eng <= 89) {
eng_80_89++;
}
if (scores[i]->cs >= 80 && scores[i]->cs <= 89) {
cs_80_89++;
}
if (scores[i]->math >= 90) {
math_90++;
}
if (scores[i]->eng >= 90) {
eng_90++;
}
if (scores[i]->cs >= 90) {
cs_90++;
}
}
printf("数学:\n平均分:%.2f 最高分:%d 最低分:%d 不及格人数:%d "
"60-69分人数%d "
"70-79分人数%d 80-89分人数%d 90分以上人数%d\n",
math_sum * 1.0 / n, math_max, math_min, math_fail, math_60_69,
math_70_79, math_80_89, math_90);
printf("英语:\n平均分:%.2f 最高分:%d 最低分:%d 不及格人数:%d "
"60-69分人数%d "
"70-79分人数%d 80-89分人数%d 90分以上人数%d\n",
eng_sum * 1.0 / n, eng_max, eng_min, eng_fail, eng_60_69, eng_70_79,
eng_80_89, eng_90);
printf("计算机:\n平均分:%.2f 最高分:%d 最低分:%d 不及格人数:%d "
"60-69分人数%d "
"70-79分人数%d 80-89分人数%d 90分以上人数%d\n",
cs_sum * 1.0 / n, cs_max, cs_min, cs_fail, cs_60_69, cs_70_79,
cs_80_89, cs_90);
// 根据姓名或学号查询某人的各门成绩,重名情况也能处理。
while (1) {
printf("请输入学号或姓名:\n");
char *query = (char *)malloc(sizeof(char) * 105);
scanf("%s", query);
int found = 0;
for (int i = 0; i < n; i++) {
if (strcmp(scores[i]->uid, query) == 0 ||
strcmp(scores[i]->name, query) == 0) {
printf("%s %s %d %d %d\n", scores[i]->uid, scores[i]->name,
scores[i]->math, scores[i]->eng, scores[i]->cs);
found = 1;
}
}
if (!found) {
printf("未找到\n");
}
}
return 0;
}
```
## 输入
```
7
001 L1 78 77 90
002 L2 89 67 88
003 L3 56 66 78
004 L4 89 86 85
005 L5 67 88 76
006 L6 45 54 67
007 L6 78 76 70
```
## 输出
```
Total: 7
当前数据:
001 L1 78 77 90
002 L2 89 67 88
003 L3 56 66 78
004 L4 89 86 85
005 L5 67 88 76
006 L6 45 54 67
007 L6 78 76 70
--------------------
按照数学成绩排序:
002 L2 89 67 88
004 L4 89 86 85
001 L1 78 77 90
007 L6 78 76 70
005 L5 67 88 76
003 L3 56 66 78
006 L6 45 54 67
--------------------
按照英语成绩排序:
005 L5 67 88 76
004 L4 89 86 85
001 L1 78 77 90
007 L6 78 76 70
002 L2 89 67 88
003 L3 56 66 78
006 L6 45 54 67
--------------------
按照计算机成绩排序:
001 L1 78 77 90
002 L2 89 67 88
004 L4 89 86 85
003 L3 56 66 78
005 L5 67 88 76
007 L6 78 76 70
006 L6 45 54 67
--------------------
平均分:
004 L4 89 86 85 86.67
002 L2 89 67 88 81.00
001 L1 78 77 90 81.00
005 L5 67 88 76 77.00
007 L6 78 76 70 74.67
003 L3 56 66 78 66.00
006 L6 45 54 67 55.00
--------------------
数学:
平均分71.71 最高分89 最低分45 不及格人数2 60-69分人数1 70-79分人数2 80-89分人数2 90分以上人数0
英语:
平均分73.43 最高分88 最低分54 不及格人数1 60-69分人数2 70-79分人数2 80-89分人数2 90分以上人数0
计算机:
平均分79.14 最高分90 最低分67 不及格人数0 60-69分人数1 70-79分人数3 80-89分人数2 90分以上人数1
请输入学号或姓名:
L1
001 L1 78 77 90
请输入学号或姓名:
L2
002 L2 89 67 88
请输入学号或姓名:
L6
007 L6 78 76 70
006 L6 45 54 67
> EOF <
```
# 实验十 一元稀疏多项式计算器
## 问题分析
这是一个一元稀疏多项式的简单计算器需要设计一种数据结构来存储多项式并实现多项式的输入输出加法减法和计算多项式在某个点的值的功能
## 解决方案
采用带表头结点的单链表来存储多项式链表的每个节点表示多项式的一项节点包含两个成员变量系数和指数
1. 输入并建立多项式从用户输入中获取系数和指数并依次插入链表中插入时需要保持链表的有序性按指数降序排列可以采用插入排序的方法
2. 输出多项式遍历链表按格式输出多项式的每一项
3. 多项式相加遍历两个链表同时比较节点的指数大小按照大小关系进行处理如果两个指数相等则将系数相加并创建新的节点插入到结果链表中如果一个指数较大则将该项直接插入结果链表中如果一个指数较小则将该项的系数取负数后插入结果链表中
4. 多项式相减与多项式相加类似不同的是需要将第二个多项式的所有系数取负数
5. 计算多项式在某个点的值遍历链表依次将每一项的系数乘以x的指数次方并累加到结果中
## 代码
```c
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int c, e;
struct Node *next;
} Node;
typedef struct List {
Node head;
int len;
} List;
// 初始化链表
List *init_list() {
List *l = (List *)malloc(sizeof(List));
l->head.next = NULL;
l->len = 0;
return l;
}
// 降序插入
void insert(List *l, int c, int e) {
Node *p = &(l->head);
while (p->next && p->next->e > e) {
p = p->next;
}
if (p->next && p->next->e == e) {
p->next->c += c;
if (p->next->c == 0) {
Node *q = p->next;
p->next = q->next;
free(q);
l->len--;
}
} else {
Node *q = (Node *)malloc(sizeof(Node));
q->c = c, q->e = e;
q->next = p->next;
p->next = q;
l->len++;
}
}
// 只输出
void output(List *l) {
Node *p = l->head.next;
printf("%d,", l->len);
while (p) {
printf("%d,%d", p->c, p->e);
p = p->next;
if (p)
printf(",");
}
printf("\n");
}
// 只输出
void plus(List *l1, List *l2) {
Node *p1 = l1->head.next, *p2 = l2->head.next;
while (p1 && p2) {
if (p1->e > p2->e) {
printf("%d,%d,", p1->c, p1->e);
p1 = p1->next;
} else if (p1->e < p2->e) {
printf("%d,%d,", p2->c, p2->e);
p2 = p2->next;
} else {
printf("%d,%d,", p1->c + p2->c, p1->e);
p1 = p1->next;
p2 = p2->next;
}
}
while (p1) {
printf("%d,%d,", p1->c, p1->e);
p1 = p1->next;
}
while (p2) {
printf("%d,%d,", p2->c, p2->e);
p2 = p2->next;
}
printf("\n");
}
// 只输出
void minus(List *l1, List *l2) {
Node *p1 = l1->head.next, *p2 = l2->head.next;
while (p1 && p2) {
if (p1->e > p2->e) {
printf("%d,%d,", p1->c, p1->e);
p1 = p1->next;
} else if (p1->e < p2->e) {
printf("%d,%d,", -p2->c, p2->e);
p2 = p2->next;
} else {
printf("%d,%d,", p1->c - p2->c, p1->e);
p1 = p1->next;
p2 = p2->next;
}
}
while (p1) {
printf("%d,%d,", p1->c, p1->e);
p1 = p1->next;
}
while (p2) {
printf("%d,%d,", -p2->c, p2->e);
p2 = p2->next;
}
printf("\n");
}
int calc(List *l, int x) {
int sum = 0;
Node *p = l->head.next;
while (p) {
sum += p->c * pow(x, p->e);
p = p->next;
}
return sum;
}
int main() {
// freopen("data.in", "r", stdin);
List *l = init_list();
List *l2 = init_list();
int n1, n2, c, e;
scanf("%d", &n1);
for (int i = 0; i < n1; i++) {
scanf("%d%d", &c, &e);
insert(l, c, e);
}
scanf("%d", &n2);
for (int i = 0; i < n2; i++) {
scanf("%d%d", &c, &e);
insert(l2, c, e);
}
output(l);
output(l2);
plus(l, l2);
minus(l, l2);
while (1) {
printf("请输入x的值");
scanf("%d", &c);
if (c == -1)
break;
printf("%d\n", calc(l, c));
}
return 0;
}
```
## 输入
```
4
1 2
5 2
3 4
666 9
2
1 3
2 4
```
## 输出
```
3,666,9,3,4,6,2
2,2,4,1,3
666,9,5,4,1,3,6,2,
666,9,1,4,-1,3,6,2,
请输入x的值1
675
请输入x的值2
341064
请输入x的值3
13109175
```
# 实验十三 迷宫问题
## 问题描述:
迷宫实验是取自心理学的一个古典实验。在该实验中,把一只老鼠从一个无顶大盒子的门放入,在盒中设置了许多墙,对行进方向形成了多处阻挡。盒子仅有一个出口,在出口处放置一块奶酪,吸引老鼠在迷宫中寻找道路以到达出口。对同一只老鼠重复进行上述实验,一直到老鼠从入口到出口,而不走错一步。老鼠经多次试验终于得到它学习走迷宫的路线。
## 设计功能要求:
迷宫由m行n列的二维数组设置0表示无障碍1表示有障碍。设入口为11出口为mn每次只能从一个无障碍单元移到周围四个方向上任一无障碍单元。编程实现对任意设定的迷宫求出一条从入口到出口的通路或得出没有通路的结论。
算法输入:代表迷宫入口的坐标
算法输出:穿过迷宫的结果。 算法要点:创建迷宫,试探法查找路。
## 思路
1. 用二维数组存储迷宫0表示无障碍1表示有障碍。
2. 从入口开始,按照顺时针方向依次尝试,如果能走通就继续,否则回溯。
3. 如果走到出口,就输出路径。
4. 如果回溯到入口,就说明没有路径。
## 代码
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int m, n;
int maze[105][105];
// 存储当前路径
char *path[10005];
int path_len = 0;
void dfs(int x, int y) {
if (x == m && y == n) {
// 找到出口
for (int i = 0; i < path_len; i++) {
printf("%s", path[i]);
if (i != path_len - 1) {
printf("->");
}
}
printf("\n");
return;
}
maze[x][y] = 1;
// 四个方向依次尝试
if (x + 1 <= m && maze[x + 1][y] == 0) {
path[path_len] = (char *)malloc(sizeof(char) * 105);
sprintf(path[path_len++], "(%d, %d)", x + 1, y);
dfs(x + 1, y);
path_len--;
}
if (y + 1 <= n && maze[x][y + 1] == 0) {
path[path_len] = (char *)malloc(sizeof(char) * 105);
sprintf(path[path_len++], "(%d, %d)", x, y + 1);
dfs(x, y + 1);
path_len--;
}
if (x - 1 >= 1 && maze[x - 1][y] == 0) {
path[path_len] = (char *)malloc(sizeof(char) * 105);
sprintf(path[path_len++], "(%d, %d)", x - 1, y);
dfs(x - 1, y);
path_len--;
}
if (y - 1 >= 1 && maze[x][y - 1] == 0) {
path[path_len] = (char *)malloc(sizeof(char) * 105);
sprintf(path[path_len++], "(%d, %d)", x, y - 1);
dfs(x, y - 1);
path_len--;
}
}
int main() {
freopen("data.in", "r", stdin);
scanf("%d%d", &m, &n);
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
scanf("%d", &maze[i][j]);
}
}
path[path_len] = (char *)malloc(sizeof(char) * 105);
strcpy(path[path_len++], "(1, 1)");
dfs(1, 1);
return 0;
}
```
## 输入
```
7 6
0 0 1 1 0 0
1 0 0 0 0 0
1 0 0 0 0 1
1 0 0 0 0 1
1 1 0 0 0 0
1 1 1 1 0 0
```
## 输出
```
(1, 1)->(1, 2)->(2, 2)->(3, 2)->(4, 2)->(4, 3)->(5, 3)->(5, 4)->(5, 5)->(6, 5)->(7, 5)->(7, 6)
(1, 1)->(1, 2)->(2, 2)->(3, 2)->(4, 2)->(4, 3)->(5, 3)->(5, 4)->(5, 5)->(6, 5)->(6, 6)->(7, 6)
```
# 实验感悟
通过本学期的数据结构实验, 我对数据结构有了更深的理解, 也对C语言有了更深的理解. 通过实验, 我学会了如何使用C和C++语言来实现各种数据结构, 也学会了如何使用各种数据结构来解决实际问题.
# 更多
Qt 版本的电子表格等待制作中, 预计在假期尝试.