2022西北农林科技大学信息工程学院C语言阶段二题解(NWAFU-oj)

PS:本题解是直接粘贴oj上通过了的代码,也就是考场上做出来的。部分方法比较笨,也是考场上我的第一反应,敬请谅解。

问题 A: 非线性方程牛顿法求解

时间限制: 1 Sec 内存限制: 128 MB

提交: 1130 解决: 112

[提交][状态][讨论版]

题目描述

用指向函数的指针设计通用非线性方程牛顿法求解函数Newton(f,df,x),求任意非线性方程f(x)=0在初始值x0附近的近似解,要求近似解精确到epsilon(1E-5)。其原型如下:

double Newton (double (*fun)(double), double (*dfun)(double),double x0);

其中,fun是指向原函数f(x)的函数指针,dfun是指向导函数f'(x)的函数指针,x0是求解方程近似解的初始值。

说明:非线性方程牛顿法求解公式如下:

xk+1 = xk - f(xk)/ f'(xk)

其中,xk表示第k步的x值,xk+1表示第k+1步的x值,f(xk)为原函数在xk处的值,f'(xk)为导函数在xk处的值。

当xk+1-xk的绝对值小于epsilon时,结束迭代,得到近似解xk+1。当在xk处的导函数值小于epsilon时,认为零作为了除数,直接返回INF(即return 1.0/0.0;,该语句用于返回INF)。

在已给出的代码中,f1()、df1()、f2()、df2()、f3()和df3()6个C语言函数分别用于计算如下3个数学函数f1(x) = x3 - 10x2 + 3x + 20、f2(x) = x3 - 6x - 1f3(x) = x3 + 10x - 20及其导函数的在指定x处的值。

程序主框架如下(提交时不要包含在内):

#include 
#include 
#include 

double f1(double);
double df1(double);
double f2(double);
double df2(double);
double f3(double);
double df3(double);
double Newton(double (*fun)(double), double (*dfun)(double), double x0);

 int main() 
{ 
      double x0;
       int n; 

      scanf("%d%lf",&n,&x0); 
      switch(n) 
      { 
      case 1: 
              printf("%.5f\n", Newton(f1,df1,x0)); 
              break; 
      case 2: 
              printf("%.5f\n", Newton(f2,df2,x0)); 
              break; 
      case 3: 
              printf("%.5f\n", Newton(f3,df3,x0)); 
              break; 
      default: 
              printf("error1\n"); 
              exit(0); 
      } 
      return 0; 
} 

double f1(double x) { 
      return x*x*x-10*x*x+3*x+20.0; 
} 

double df1(double x) { 
      return 3*x*x-20*x+3; 
}

double f2(double x) { 
      return x*x*x-6*x-1; 
}

double df2(double x) { 
      return 3*x*x-6; 
}

double f3(double x) { 
      return x*x*x+10*x-20; 
}

double df3(double x) { 
      return 3*x*x+10; 
}

输入

一个整数和一个双精度数,其中整数代表要求解的非线性方程的编号(1~3),双精度数为方程求解x0的初始值

输出

方程的近似解,精确到小数点后5位

说明:输出格式由题目框架设定,无需关注输出格式。

样例输入

1 -1

样例输出

-1.20865

题解

double Newton (double (*fun)(double), double (*dfun)(double),double x0){
    double x1=0,s=0;
    s=fun(x0)/dfun(x0);
    x1=x0-s;
    if(fabs(dfun(x0)) < 1e-5)
        return 1.0/0.0;
    if(fabs(s) < 1e-5)
        return x1;
    else{
        x0=x1;
        return Newton(fun,dfun,x0);
    }
}

反思

本题卡的两个点:

  1. 在最后递归时要记得把x1的值传给新的x0,让新x0等于旧x1,让x0更新换代起来;

  1. 此题一开始我在本地测试答案一直都是-1.0000,原因是一开始的先入为主,把x1定义为整型变量了,那么返回的x1值就小数点不可能有数字了,注意x1、s要定义为double型;

注意:本题主函数中有库,fabs函数(绝对值函数)直接用,并且用对;

问题 B: Luhn算法证件号码校验

时间限制: 1 Sec 内存限制: 128 MB

提交: 1474 解决: 157

[提交][状态][讨论版]

题目描述

Luhn算法是一种广泛使用的证件号码(由若干数字组成,长度不定)“模10”校验算法。其计算过程为:

1、从卡号最未位数字开始,逆向将奇数位(1、3、5等)求和。

2、从卡号最未位数字开始,逆向将偶数位数字乘2后求和(说明:如果乘2得到的是两位数,则将乘2的结果减9后参与求和)。

3、将奇数位和与偶数位和相加,通过判断相加结果是否是10的整数倍实现校验。

例如对于卡号5432123456788881,其各位数字编号如下表所示:

则其“模10”校验计算过程为:

1、奇数位和 = 1+8+8+6+4+2+2+4 = 35

2、偶数位和 = (8*2-9)+(8*2-9)+(7*2-9)+(5*2-9)+(3*2)+(1*2)+(3*2)+(5*2-9)

= 7+7+5+1+ 6+2+6+1 = 35

3、奇数位和+偶数位和=35+35=70, 结果是10的整数倍,校验通过。

请编写函数CheckLuhn(),完成若干组证件号码的Luhn校验,要求函数原型为:

int CheckLuhn(const char * cardId , int sum[2]);

其中,cardId是待校验证件号码字符串,sum是由两个元素构成的整型数组,sum[0]用于存储奇数位和,sum[1]用于存储偶数位和。如果通过校验,函数返回0,否则返回1。

请在已给出的宏定义、函数原型、驱动main函数代码的基础上完成题目。

程序主框架如下(注意,只提交题目要求的函数):

#include 
#include 
#include 

#define MAX_LEN 128

int CheckLuhn ( const char * cardId, int sum[2] );
int ReadLine(char *s);

int ReadLine(char *s)
{
    int n = 0, ch = 0;
    while((ch = getchar()) != '\n' && ch != EOF && n < MAX_LEN - 1)
        s[n++] = ch;
    s[n]='\0';
    return n;
}

int main()
{
    int s[2]={0}, r;
    char id[MAX_LEN];

    while (ReadLine(id) > 0) {
        r = CheckLuhn(id, s);
        printf("%s,R=%d\t%d\t%d\n", id, r, s[0], s[1]);
    }
    return 0;
}

输入

若干行号码字符串

输出

四部分内容。第一部分为校验号码,第二部分为校验结果,第三、第四部分分别为奇数位和与偶数位和。

说明:输出格式由题目框架设定,无需关注输出格式。

样例输入

4342560238811238

样例输出

4342560238811238,R=0 32 38

提示

注意:在设计的函数中不需要进行任何数据输出,如需查看运算结果,请在提交前删除相关数据输出代码。

题解

int CheckLuhn(const char * cardId , int sum[2]){
    int n,i,mn=0,s=0;
    sum[0]=sum[1]=0;
    n=strlen(cardId);
    if(n%2 == 0){
    for(i = 0;i < n;i++){
        if(i%2 != 0){
            sum[0]=sum[0]+cardId[i]-48;
        }
        if(i%2 == 0){
            if(cardId[i] >= '5')
                mn=(cardId[i]-48)*2-9;
            else mn=(cardId[i]-48)*2;
            sum[1]=sum[1]+mn;
        }
    }
    s=sum[0]+sum[1];
    if(s % 10 == 0)
        return 0;
    else return 1;
    }
    if(n%2 != 0){
        for(i = 0;i < n;i++){
            if(i%2 == 0){
                sum[0]=sum[0]+cardId[i]-48;
            }
            if(i%2 != 0){
                if(cardId[i] >= '5')
                    mn=(cardId[i]-48)*2-9;
                else mn=(cardId[i]-48)*2;
                sum[1]=sum[1]+mn;
            }
        }
    }
        s=sum[0]+sum[1];
        if(s % 10 == 0)
            return 0;
        else return 1;
}

反思

本题卡的点:

一定一定在卡的时候要认真读题,认真看题目给的图。对应好每一位。一开始我只考虑了题目给的两个样例,这两个样例恰好都是16位(偶数位),我的错误代码就都跑的通,但提交之后答案错误。我就自己试图写了个样例“123”,一共有3位,发现到奇数位个码的话,跟偶数位就恰好是相反的(因为指针第一个数一直都是从0开始的),这个时候要把奇数位重新考虑,就只是跟偶数位的思路一样,反一下就行。

注意:本题输入的是字符串,运算时数字对应的是其ASCII码值,所以记得减去48得到数字本身值。

问题 C: 机动车车辆限行检测

时间限制: 1 Sec 内存限制: 128 MB

提交: 989 解决: 98

[提交][状态][讨论版]

题目描述

机动车车牌由3部分组成,分别是:省份简称、地级市字母代码、6位或5位车牌号(由大写字母和数字组成,新能源汽车为6位车牌号,普通车辆为5位车牌号),车牌号至少包含一位数字。根据车牌号实现机动车车辆限行,其规则如下: 新能源汽车不限号;普通车辆星期一限行尾号1和6,星期二限行尾号2和7,星期三限行尾号3和8,星期四限行尾号4和9,星期五限行尾号5和0,星期六和星期天所有车辆均不限行。

车牌尾号为车牌中数字的最后一位,如果车牌号最后一位不是数字,则从后往前找到的第一个数字即为车牌尾号。例如03D4J的尾号为4。

现要求编写机动车车辆限行检测函数,其原型如下:

int Judge(char *plate_num, int day);

其中,plate_num是用来存储车牌号的字符串指针,day代表星期的整型量(1~7分别代表星期一到星期日)。

当plate_num(长度小于5或大于6、不包含数字、包含非法字符)或day(小于1或大于7)错误时,返回-1;当该车牌号被限行时,返回1,否则返回0。

主程序框架如下:

#include 
#include 

#define MAX_LEN 10

int Judge(char *, int);
int ReadLine(char *s);

int ReadLine(char *s)
{
    int n = 0, ch = 0;
    while((ch = getchar()) != '\n' && ch != EOF && n < MAX_LEN - 1)
        s[n++] = ch;
    s[n]='\0';
    return n;
}

int main()
{
    char str[MAX_LEN];
    int n;

    while (ReadLine(str) > 0){
        scanf("%d", &n);   /* 星期 */
        getchar();
        printf("%d\n", Judge(str, n));
    }
    return 0;
}

输入

输出

说明:输出格式由题目框架设定,无需关注输出格式。

提示

注意:在设计的函数中不需要进行任何数据输出,如需输出运算结果,请在提交前删除相关代码.

题解

int Judge(char *plate_num, int day){
    int n,i,j,k;
    n=strlen(plate_num);
    if(n<5 || n>6 || day<1 ||day>7)
        return -1;
    for(i = 0;i < n;i++){
        if(plate_num[i] < '0' || (plate_num[i] > '9' && plate_num[i] < 'A')||plate_num[i] > 'Z')
            return -1;
    }
    for(j = 0;j < n;j++){
        if(plate_num[j] >= '0' && plate_num[j] <= '9')
            break;}
        if(j == n)
            return -1;
    if(n == 6)
        return 0;
    if(n == 5){
        for(k = 4;k >= 0;k--){
            if(plate_num[k] >= '0' && plate_num[k] <= '9'){
            if(day == 1){
                if(plate_num[k] == '1' || plate_num[k] == '6')
                    return 1;
                else return 0;}
            if(day == 2){
                if(plate_num[k] == '2' || plate_num[k] == '7')
                    return 1;
                else return 0;}
            if(day == 3){
                if(plate_num[k] == '3' || plate_num[k] == '8')
                    return 1;
                else return 0;}
            if(day == 4){
                if(plate_num[k] == '4' || plate_num[k] == '9')
                    return 1;
                else return 0;}
            if(day == 5){
                if(plate_num[k] == '5' || plate_num[k] == '0')
                    return 1;
                else return 0;}
            if(day == 6 || day == 7)
                return 0;
            }
        }
    }
}

反思

本题是我做出来三道题中,第一个有思路的,也是第一个做的题(我是从后往前做的)

本题卡的点:

一开始注意到了“从后往前找到的第一个数字即为车牌尾号”自己写代码的时候就又忘了,本题也没有输入,就硬着头皮交了一次,显示通过了80%,后来第一反应到自己是这错了把for循环条件改了一下,还好反应的快,没有了浪费时间。

其他这道题就没有什么了,疯狂码字而已,注意n,day,plate_num[]分别代表什么别搞混就行。注意for(i = n;i >= 0;i--)就好,此次考试两题的for循环都是从后往前,打破以往常规。

问题 D: 水果店库存排序

时间限制: 1 Sec 内存限制: 128 MB

提交: 207 解决: 20

[提交][状态][讨论版]

题目描述

某水果店使用文本文件记录每种水果的进货和出货量(单位:公斤),文件中的每一行表示一种水果记录,各行第一列表示水果名称,第二列、第三列分别表示进货量和出货量,各列间用空白分隔。假设水果已归类,不存在重复现象。例如:

Apple 50.5 20.3

Banana 30 10.2

Orange 100 80.6

...

为实现水果店库存排序,要求编写如下两个函数:

1、文件读取函数,其原型如下:

int loadStock(pFInfo stocks, const char *fname);

其中,stocks是结构体类型的指针,用于存储读取的库存记录,fname是需要读取的水果库存记录文本文件的文件名指针,如"record.txt"。函数返回成功读取的记录条数。假定stocks指针指向的内存空间已经预先分配。

2、按照水果库存量(库存量=进货量-出货量)降序对记录进行排序并输出到指定文件的函数,其原型如下:

void sortAndSaveStock(pFInfo stocks, int n, char *fname);

其中,stocks是结构体类型的指针,用于存储读取的库存记录,n是水果库存记录总数,fname是指定的输出文本文件的文件名指针,如"sorted_record.txt"。输出时所有小数保留1位小数。

文本文件样例可使用wget命令在SSH环境中获取,命令: wget http://202.117.179.201/attach/record.txt

程序主体框架如下(注意只提交题目要求的函数):

#include 
#include 

#define MAX_NAME_LEN 20
#define MAX_FRUIT_LEN 20

typedef struct{
    char name[MAX_NAME_LEN];
    float in;
    float out;
}FInfo, *pFInfo;

int loadStock(pFInfo, const char *);
void sortAndSaveStock(pFInfo, int, char *);

int main(int argc, char *argv[])
{
    FInfo fruitStock[MAX_FRUIT_LEN];

    if (argc < 3)
    {
        printf("Usage: %s  \n", argv[0]);
        exit(EXIT_FAILURE);
    }
    /* read stock information from txt file */
    int n = loadStock(fruitStock, argv[1]);

    if ( n > 0)  sortAndSaveStock(fruitStock, n, argv[2]);
    return 0;
}  

输入

输出

说明:在设计的函数中无需任何控制台输出,文件输出时所有数值量均保留一位小数,水果名称、进货量与出货量间用一个空格分隔,每个水果一行,最后一行无换行

提示

注意:在设计的函数中不需要进行任何数据输出至显示器,如需在显示器上输出运算结果,请在提交前删除相关代码。 在文件写入的时候,注意不要加入任何多余的字符(回车、换行及空格等)。

问题 E: 英文句子按单词实现反转

时间限制: 1 Sec 内存限制: 128 MB

提交: 110 解决: 14

[提交][状态][讨论版]

题目描述

现需要对一个以'.'、'?'或'!'为终止字符的英文句子按单词实现反转。现假定:

1、每次反转只针对一行完整的句子,如果输入不是一个完整句子,则输出"Sentence needs a terminating character. (./?/!)\n\n";

2、所有句子均无前导后续分隔符;

3、除终止符和分隔符外,其它标点符号与单词被认为是一个整体,并且不能独立存在。

如:

原文为:I like C langage, but he is only interested in football.

反转为:football in interested only is he but langage, C like I.

程序主体已经完成,要求实现如下两个函数:

1、单词分割及提取函数,其原型如下:

char ** splitSentenceWords(char *str , const char *delim, size_t n);

其中,str为指向句子原文字符串的指针,delim为指向可能出现的单词分隔符构成的字符串的指针,n为单词个数。函数返回分割结果的字符型二级指针。函数调用完成后,必须手工释放返回的二级指针指向的指针数组占用的内存。

2、句子按单词反转函数,其原型如下:

char * revSentenceWords(char *str, const char *delim, const char *hyphen);

其中,str为指向句子原文字符串的指针,delim为指向可能出现的单词分隔符构成的字符串的指针,hyphen指向结果句子中连接各单词的字符串。函数返回指向反转结果的指针。函数调用完成后,必须手工释放返回的指针指向的字符串占用的内存。

程序主体框架如下(注意只提交自己补足的函数代码,下面的代码请勿提交):

#include 
#include 
#include 

#define MAXLENGTH 1024

char* revSentenceWords(char *, const char *, const char *);
char** splitSentenceWords(char *, const char *, size_t);
char isSentence(const char *, const char *);
size_t getWordsCnt(const char *, const char *);

int ReadLine(char *, int);

int ReadLine(char *str, int len)
{
    int n = 0, ch = 0;
    while((ch = getchar()) != EOF && ch != '\n' && n < len - 1)
        str[n++] = ch;
    str[n]='\0';
    return n;
}

int main(void)
{
    char s[MAXLENGTH];
    char *p,**q;
    char delim[] = " \t\n\r";
    char term[] = ".?!";
    char hyphen[] = " ";
    int i, wc = 0;

    ReadLine(s, MAXLENGTH);
    if (isSentence(s, term))
    {
        p = revSentenceWords(s, delim, hyphen);
        puts(p);
        wc = getWordsCnt(p, delim);
        printf("WordsCnt= %d\n", wc);
        q = splitSentenceWords(p, delim, wc);
        for(i = 0; i < wc; i++)
        {
            puts(q[i]);
        }
        free(q);
        free(p);
    }
    return(0);
}

char isSentence(const char *s, const char *term)
{
    char terminator = 0;
    size_t len = 0;

    len = strlen(s);
    terminator = s[len - 1];

    if(!strchr(term, terminator))
    {
        printf("Sentence needs a terminating character. (./?/!)\n\n");
        exit(1);
    }

    return terminator;
}

size_t getWordsCnt(const char *s, const char *delim)
{
    size_t cnt = 0;

    while(*s)
    {
        if((!strchr(delim, *s) && (strchr(delim, *(s + 1)))) || (*(s + 1) == '\0' && (!strchr(delim, *s))))
        {
            cnt++;
        }
        s++;
    }
    return cnt;
}

输入

输出

说明:输出格式由题目框架设定,无需关注输出格式。

提示

注意:在设计的函数中不需要进行任何数据输出,如需输出运算结果,请在提交前删除相关代码。

无题解

D、E两题考场上完全没有思路,总结来看还是对结构和文件这两张没学扎实。把问题先放到这儿,期待日后某天有能力再来解决这两道题

你可能感兴趣的:(oj题解,考试题解,c语言,算法,c语言,c++)