A | 行程长度编码 |
题目描述
“行程长度编码(Run-Length Encoding,RLE)”是一种无损压缩编码方法,其核心思想是依次记录符号序列中的每个字符(忽略大小写)及其重复出现的次数。例如用户输入字符序列(字符串)AaABBBbCBB,则其RLE编码的结果为3A4B1C2B。
下面的代码实现对输入字符串的RLE编码,现要求在函数int rle(char *code, const char *seq);及 函数readSeq(char *seq, int n);定义中的五个空白处填充适当的表达式,完成程序功能。
其中:rle()函数形参seq为长度在(0, 80]间(strlen(seq)>0&&strlen(seq)≤80)的全部由大小写英文字母组成的字符串指针,形参code为RLE编码后的结果字符串指针。编码正常结束函数返回0;如字符串指针seq为空或字符串长度不合法时,不进行编码函数直接返回-1;当字符串seq中出现非英文字母的其他字符时,也不进行编码函数直接返回-2。
readSeq()从键盘读取最多n个字符到seq字符串中,以回车符或EOF字符为结束标志。
请使用wget从http://202.117.179.201/attach/P1551.c获取需要填空的代码。完成设计rle()、readSeq()等函数,并在本地完成测试后,仅提交rle()、readSeq()函数及其调用的自定义函数的实现代码。
#include <stdio.h>
#include <string.h>
#define MAX_SEQ_LEN 80
int rle(char *code, const char *seq);
int readSeq(char *seq, int n);
int main()
{
char seq[MAX_SEQ_LEN + 1] = { 0 };
char code[2 * MAX_SEQ_LEN + 1];
int r;
readSeq(seq, MAX_SEQ_LEN);
r = rle(code, seq);
switch (r) {
case 0:
printf("%s\n", code);
break;
case -1:
printf("Length is inValid.\n");
break;
case -2:
printf("Has inValid character.\n");
break;
}
return 0;
}
/* 以上代码禁止提交 */
int readSeq(char *seq, int n)
{
int ch, k;
k = 0;
while ((ch = getchar())) {
if (__________(1)_________)
break;
seq[k++] = ch;
}
_____(2)______;
return k;
}
int rle(char *code, const char *seq)
{
int i, len = 0;
char *p;
if (__________(3)____________)
return -1;
p = (char *)seq;
len = 0;
while (____(4)____) {
char token = *p;
i = 1;
token &= 0xDF;
if (token > 'Z' || token < 'A')
return -2;
while (_________(5)__________) {
i++;
}
len = sprintf(code, "%d%c", i, token);
code += len;
}
*code = '\0';
return 0;
}
样例输入
KAAAEE
样例输出
1K3A2E
答案如下
int readSeq(char *seq, int n)
{
int ch, k;
k = 0;
while ((ch = getchar())) {
if (k == n || ch =='\n' || ch == EOF)
break;
seq[k++] = ch;
}
seq[k] = '\0';
return k;
}
int rle(char *code, const char *seq)
{
int i, len = 0;
const char *p;
if (seq == NULL || strlen(seq) == 0 || strlen(seq) > 80)
return -1;
p = seq;
len = 0;
while (*p) {
char token = *p;
i = 1;
token &= 0xDF;
if (token > 'Z' || token < 'A')
return -2;
while (*++p && (*p & 0xDF) == token) {
i++;
}
len = sprintf(code, "%d%c", i, token);
code += len;
}
*code = '\0';
return 0;
}
B | 成绩排序 |
题目描述
从二进制文件“stud.dic”中读取指定范围内的学生数据,并使用结构体、文件操作、函数指针等实现对各科成绩的升、降序通用化排序。程序主体框架已完成,请仔细阅读已有代码,并完成下面的两个函数:
1.通用排序
函数原型为:void sort(stu *stud, int mode, int sub, int n);
其中,形参sub用于指定需要排序的科目,取值为0、1或2代表的科目编号,形参mode用于指定排序模式,1表示升序,2表示降序,从而实现对形参n指定的n个学生成绩进行排序,形参stud是需要参与排序学生的数组首地址指针。
2.从二进制文件读入指定数据
函数原型为:stu *read( FILE *fp, int m, int n);
其中,形参fp传入的是已打开二进制文件的文件指针,形参m用于指定范围的起始位置(从0开始编号), 形参n用于指定需要读取的学生数据个数。在函数内部,用动态内存分配存储空间,读入指定的数据到分配的空间后,返回其首地址。
注意:
在程序主体框架中,已给出了结构体、主函数、辅助函数类型定义以及所需要完成的函数原型。
不能改变程序主体框架和指定函数原型,仅需提交sort()和read()函数及其调用的自定义函数的实现代码。
在二进制文件中,学生信息最多为30条,且姓名中可能会有空格,如:“白 莹”,成绩中可能含有小数。
请用wget命令从http://202.117.179.201/attach/P1552.c下载程序主体框架和从http://202.117.179.201/attach/stud.dic下载学生信息存储文件“stud.dic”。在完成设计sort()、read()等函数设计,在本地完成测试后,再提交sort()、read()函数及其调用的自定义函数的实现代码。
程序框架代码:
#include <stdio.h>
#include <stdlib.h>
#define M 30
typedef struct STUDENT
{
int studentID;
char studentName[10];
float score[3];
} stu, *pstu;
typedef int (*CmpFunPtr)(float, float); /* 定义函数指针类型CmpFunPtr,该类型的指针指向的函数应该返回一个整型数据,且含有两个float型形参。 */
int compareA(float, float); /* 降序排序比较函数 */
int compareD(float, float); /* 升序排序比较函数 */
stu *read(FILE *fp, int m, int n);
void sort(stu *stud, int mode, int sub, int n);
void print(pstu stud, int n);
CmpFunPtr setmode(int mode); /* 返回一个CmpFunPtr类型的函数指针 */
int main()
{
struct STUDENT *stud;
FILE *fp;
int m, n, mode, sub;
scanf("%d%d%d%d",&m, &n, &mode, &sub);
if(m < 0 || m > M || n < 0 || n+m > M || (mode != 1 && mode != 2) || (sub != 0 && sub !=1 && sub != 2))
{
printf("error!\n");
exit(0);
}
fp = fopen("stud.dic", "rb");
if(fp == NULL )
exit(0);
stud=read(fp, m, n);
sort(stud, mode, sub, n);
print(stud, n);
free(stud);
fclose(fp);
return 0;
}
CmpFunPtr setmode(int mode)
{
if (mode == 1)
return compareD;
else
return compareA;
}
void print(pstu stud, int n)
{
int i;
for(i = 0; i < n; i++)
{
printf("%dth: %d %s %.2f %.2f %.2f\n", i, stud[i].studentID,
stud[i].studentName, tud[i].score[0], stud[i].score[1], stud[i].score[2]);
}
}
int compareA(float x, float y)
{
return x > y;
}
int compareD(float x, float y)
{
return x < y;
}
/* 以上代码禁止提交 */
输入
起始学生序号m,学生数量n,排序方式mode,排序科目编号sub
输出
从第m个开始的n个学生信息,并以mode方式对科目sub排序
样例输入
0 3 1 0
样例输出
0th: 2009011360 卢 莹 13.27 85.45 26.72
1th: 2010036201 张 阳 87.10 54.00 63.90
2th: 2010036209 赵扶强 90.50 54.00 65.00
答案如下
struct STUDENT *read( FILE *fp, int m, int n)
{
pstu stud;
stud =(pstu)malloc(n*sizeof(stu));
fseek(fp, (long)m * sizeof(stu), SEEK_SET);
fread(stud, sizeof(stu), n, fp);
return stud;
}
void sort(stu *stud, int mode, int sub, int n)
{
int i,j,k;
stu temp;
CmpFunPtr comp = setmode(mode);
/* 使用选择排序法进行排序。也可以用冒泡排序法。因为排序字段与变量相关,不宜使用快速排序法。 */
for (i = 0; i < n-1; i++)
{
k = i;
for (j = i+1; j < n; j++)
{
if(comp(stud[j].score[ sub ], stud[k].score[ sub ]))
k = j;
}
if ( k != i )
{
temp = stud[i];
stud[i] = stud[k];
stud[k] = temp;
}
}
}
C | 日期天数距离计算 |
题目描述
假定有下面的结构体:
typedef struct {
unsigned int year; /* 有效年份 1900~2050 年*/
unsigned int month;
unsigned int day;
} Date, *PDate;
请实现下面的两个函数,其原型要求如下:
int ToDate(char *str, PDate pd);
int DaysCount(PDate sdate, PDate edate);
ToDate()函数将字符串str中的合法日期存入指针变量pd指向的结构体成员中,其中字符串str的基本形式为“YYYY-MM-DD”,YYYY代表年,MM代表月,DD代表日,如str为“2023-12-30”,则ToDate()函数的功能是将2023、12、30分别存入pd->year、pd->month、pd->day中。如果str中的日期数值范围或数据格式非法,函数返回-1,正常返回0。
DaysCount()函数计算两个日期sdate和edate之间差距是多少天,并返回计算结果。
注意:
不能改变程序主体框架和指定函数原型,仅需提交ToDate()和DaysCount()函数及其调用的自定义函数的实现代码。
请用wget命令从http://202.117.179.201/attach/P1553.c下载程序主体框架。
程序主体框架:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
unsigned int year;
unsigned int month;
unsigned int day;
} Date, *PDate;
int ToDate(char *, PDate);
int DaysCount(PDate, PDate);
int is_leap(int);
int month_days(int ,int);
int main()
{
char str1[12], str2[12];
Date sd, ed;
fgets(str1, 12, stdin);
fgets(str2, 12, stdin);
if (ToDate(str1, &sd) == 0 && ToDate(str2, &ed) == 0)
printf("%d\n", DaysCount(&sd, &ed));
else
printf("input data is invalid.\n");
return 0;
}
int is_leap(int year)
{
return ((year%4==0&&year%100!=0)||year%400==0);
}
int month_days(int month, int leap)
{
int m_days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if(month == 2)
return (leap == 1) ? 29 : 28;
else
return m_days[month];
}
/* 以上代码禁止提交 */
输入
两行形如“YYYY-MM-DD”的日期字符串,保证前一个日期早于后一个日期。
输出
两个日期间相隔的天数
样例输入
2008-08-08
2023-12-30
样例输出
5622
提示
可以使用sscanf函数从字符串str中将年月日读取为整数,例如,sscanf(str, "%d-%d-%d", &year, &month, &day)。
另外,提供以下日期计算结果,供程序测试使用。
1949年10月01日开国大典,至今(2023年12月30日)已过27118天。1950年10月25日抗美援朝,至今(2023年12月30日)已过26729天。
今天是2023年12月30日,距离西北农林科技大学90周年校庆(2024年09月10日),还有255天。
2023年09月11日开学,至今(2023年12月30日)已过110天。
答案如下
int ToDate(char *str, PDate pd)
{
int y,m,d,r;
r=sscanf(str, "%d-%d-%d", &y, &m, &d);
if (r==3) {
if (m >= 1 &&l m <= 12 && y >= 1900 && y <= 2050 && d >= 1)
{
int leap = is_leap(y);
int dd = month_days(m, leap);
if (d < dd) {
pd-&g;year = y;
pd->month = m;
pd->day = d;
return 0;
}
}
}
return -1;
}
int DayofYear(const PDate p) //计算这年过了多少天
{
int i,t = 0;
int leap = is_leap(p->year);
for(i=1; i<p->month;i++)
t += month_days(i, leap);
return t + p->day;
}
int DaysCount(PDate sdate, PDate edate)
{
int count = 0,i;
int leap;
for(i = sdate->year;i < edate->year; i++)
{
leap = is_leap(i);
count += (leap)? 366: 365;
}
count += DayofYear(edate) - DayofYear(sdate);
return count;
}
D | 数组追加填充 |
题目描述
给定一个有m个元素(m<50)的正整数整型数组nums和一个正整数k(0<k<20),现需要向nums数组中追加填充k个最小的未出现在数组nums中且互不相同的正整数,要求使填充后数组的各元素的累加和最小。请编写函数MinInsert()返回填充到nums中的k个整数之和。
函数原型:int MinInsert(int *pnums, int m, int k);
其中,形参pnums是指向nums整型数组的指针,形参m是数组元素的个数,形参k是要填充的正整数个数。
函数算法请参考下面的流程图。
注意:
不能改变指定函数原型,仅需提交MinInsert()函数及其调用的自定义函数的实现代码。
示例 1:
输入:nums = {1,4,25,10,25}, k = 2
输出:5
解释:向数组中填充的两个互不相同且未出现的正整数是 2 和 3 。nums 最终元素和为 1 + 4 + 25 + 10 + 25 + 2 + 3 = 70 ,这是所有情况中的最小值。
所以填充到数组中的两个整数之和是 2 + 3 = 5 ,所以返回 5 。
示例 2:
输入:nums = {5,6}, k = 6
输出:25
解释:向数组中填充的六个互不相同且未出现的正整数是 1 、2 、3 、4 、7 和 8 。
nums 最终元素和为 5 + 6 + 1 + 2 + 3 + 4 + 7 + 8 = 36 ,这是所有情况中的最小值。
所以填充到数组中的六个整数之和是 1 + 2 + 3 + 4 + 7 + 8 = 25 ,所以返回 25 。
请参考下面的流程图编写代码。其中,gap是数组中的间隙。要循环判定数组的间隙大小,当间隙gap大于待填数量k时,依次填入整数;否则依次填满间隙,再调整待填数量k。最后如未达到要求的待填数量,则继续在尾部填充。
答案如下
int compareFunc(void *a,void *b)
{
return (*(int *)a - *(int *)b);
}
int MinInsert(int* nums, int size, int k)
{
int left = 0;
int right = 0;
int gap;
int sum = 0;
/* 先对nums数组按从小到大排序 */
qsort(nums, size, sizeof(int) ,compareFunc);
/* 循环判定数组的间隙大小*/
for(int i =0; i < size ; i++)
{
right = nums[i];
gap = right - left -1;
if(gap > 0)
{
/* 当间隙大于待填数量时,依次填入整数并统计填入的数之和 */
if(k <= gap)
{
sum += (left*2 + 1 + k)*k/2;
return sum;
}
else
/* 否则依次填满间隙,调整k, 并统计填入的数之和 */
{
sum += (left+ right)* gap / 2;
k = k - gap;
}
}
left = right;
}
/* 如未达到要求的待填数量,则继续在尾部填充,统计填入的数之和 */
if(k)
{
sum += (left*2 + k + 1)*k / 2;
}
return sum;
}
E | 五子棋 |
题目描述
现有n×n的棋盘,棋盘上每个格子只能放入黑白两种棋子中的一个,分别用字符'+'表示黑色棋子、'*'表示白色棋子、'0'表示未着子。
编写Judge函数,Judge函数原型如下:
int Judge(char **str, int n, char ch, struct solu *ps);
其中,形参str是指向棋盘的指针,形参n表示棋盘大小(n×n),形参ch('+'、'*'分别表示黑白棋子)表示棋子颜色,形参ps是指向保存连珠方法的结构体变量。
通过调用Judge函数,判断落子布局是否可以组成五子连珠,若可以则返回1,并将找到的第一个连珠方法保存在ps指向的结构体变量中;否则返回0。struct solu结构体的定义如下:
struct solu
{
int x;
int y;
char direction;
};
其中,用x和y分别表示字符ch表示的棋子组成的第一个五子连珠的起始行号和起始列号(行号和列号均从0开始),用direction保存五子连珠的四种方向,可以在四种符号:'-','|','\','/'中取值,分别表示以x和y为起点,向右横向、向下竖向、向右下斜向、向左下斜向构成了五子连珠。
五子连珠寻找的优先级规则为:行优先(即先判断第0行的所有位置,然后是第1行、第2行……依次类推),方向优先级判断顺序为:向右横向、向下竖向、向右下斜向、向左下斜向。
请编写代码给出Judge函数的定义。编写请参考下图的处理流程。
请用wget命令从http://202.117.179.201/attach/P1555.c下载程序主体框架。 程序主体框架如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct solu
{
int x;
int y;
char direction;
};
int Judge(char **, int, char, struct solu *);
int main()
{
int n, i;
char ch;
struct solu s = {-1, -1, ' '};
scanf("%c%d", &ch, &n);
char *board[n];
for(i = 0; i < n; i++)
{
board[i] = (char *)malloc((n+1)*sizeof(char));
memset(board[i], 0, (n+1)*sizeof(char));
scanf("%s", board[i]);
}
if (Judge(board, n, ch, &s) == 1)
printf("%d %d %c\n", s.x, s.y, s.direction);
else
printf("0\n");
for(i = 0; i < n; i++)
{
free(board[i]);
}
return 0;
}
/* 以上代码禁止提交 */
输入
第1行输入一个字符ch和一个整数n。其中字符ch为'+'、'*'之一,对应表示黑子和白子。整数n表示棋盘大小为n×n。 从第2行开始有n行用'+'、'*'和' '组成的字符串,表示棋盘落子状况。
输出
两个整数和一个字符,两个整数分别表示找到的可组成五子连珠的棋子左上起始棋子的坐标,字符为'-'、'|'、'\'和'/'之一,对应五子连珠的方向。如果没有找到五子连珠,则输出0。
样例输入 复制
+ 20
0+0++*0+*+++000++0++
0+00+0+*0+*00*00***0
+0+0000+++*0+**+*0**
+**+0++*0*+0**0*0**0
*00*++0*0*++00*+00*0
*+*0*++*00*+0+0+**+0
+*+++0+0000+0***+**+
00*0+*0**+*0*000**++
*+**000+0+***++**+++
*00+*+*+00++0***0*0*
+*0**++0++*0+****00*
*+**+++0++++*0000+++
+*+000*0**++*+**+0**
+*0****+000*++++0*0*
+**0*+*00+000*0**+0+
0**+**+***0*+**00+*0
*+0++***0000+*+++0**
+0**+0+++*+0++**+000
**++++**00++00*++0*+
00*000+00++00+0+*+00
说明:分别用字符'+'、'*'、'0'代表黑子、白子、未着子,第一行输入"+ 20"表示在20×20的棋盘上对黑子是否能够组成五子连珠进行判断。
样例输出 复制
1 1 \
说明:该结果表明第1行,第1列,有一个由'+'组成的向右下斜向的五子连珠方案,是满足条件的第一个解。
提示
注意:
不能改变程序主体框架和指定函数原型,仅需提交Judge()函数及其调用的自定义函数的实现代码。
答案如下
int Judge(char **str, int n, char ch, struct solu *ps)
{
int i, j;
char t = '\0';
for(i = 0; i < n; i++) {
for(j = 0; j < n; j++)
if (str[i][j] == ch) {
if (j <= n-5 && str[i][j+1]==ch && str[i][j+2]==ch && str[i][j+3]==ch && str[i][j+4]==ch)
t = '-';
else if (i <= n-5 && str[i+1][j]==ch && str[i+2][j]==ch && str[i+3][j]==ch && str[i+4][j]==ch)
t = '|';
else if (i <= n-5 && j <= n-5 && str[i+1][j+1]==ch && str[i+2][j+2]==ch && str[i+3][j+3]==ch && str[i+4][j+4]==ch)
t ='\\';
else if (j >= 4 && i <= n-5 && str[i+1][j-1]==ch && str[i+2][j-2]==ch && str[i+3][j-3]==ch && str[i+4][j-4]==ch)
t = '/';
if (t != '\0')
{
ps->x = i;
ps->y = j;
ps->direction = t;
return 1;
}
}
}
return 0;
}