C2第二次作业

小题

2(1分)
下面四个选项中,均是正确的数值常量或字符常量的是__
Selection 1 :
(A)0.0,0f,8.9e,’&’
(B)”a”,3.9E-2.5,1e1,’\”’
(C)’3’,011,0xFF00,0a
(D)+001,0xabcd,2e2,50

解析:
A中以0开头的必须是八进制,八进制不含f,所以0f有误;
B中“a”貌似不是字符常量,而是字符串常量。不过3.9E-2.5肯定错了,因为科学计数法中e的指数必须是整数(可正可负可为零,但必须为整);
C中0a和A中的0f同一个错误

3(1分)
已知各变量的定义说明如下:
int i = 8, k, a, b;
unsigned long w = 5;
double x = 1.42, y = 5.2;
则以下符合C语言语法的表达式是__
Selection 1 :
(A)a += a -= (b = 4) * (a = 3)
(B)a = a * 3 = 2
(C)x % (-3)
(D)y = float(i)

解析:
B中把2赋值给a*3显然是不对的;
C中取余符号两端都得是整数,不过对负数取余是没问题的(取余运算无论正负都先视为两个正数的取余,最后正负号由被取余数决定:5%(-3)=2, (-5)%3=(-2), (-5)%(-3)=(-2) );
D中C语言中不存在float(),改成y=(float)i还可以接受。

5(1分)
设有以下语句:
struct st
{
int n;
struct st *next;
};
static struct st a[3] = {5, &a[1], 7, &a[2], 9, ‘\0’}, *p;
p = &a[0];
则以下表达式的值为6的是__
Selection 1 :
(A)p++->n
(B)p->n++
(C)(*p).n++
(D)++p->n

解析:
A中先是p->n结果为5,然后++加在了p上,操作之后p指向了了a[1]
B中同理,最后++加在了n上,a[0].n变成了6,但是表达式的值还是5;
C同B;
D相当于++(p->n),所以p->n为5,++之后变成了6。要注意的是->的优先级最高,++的优先级稍微低一点

7(1分)
若有定义:
char c = ‘\010’;
则变量c中包含的字符个数为_

解析:1
\010是八进制数(十进制为8),ASCII中为一个特殊字符,相当于\b(backspace,向左退一格)。用%d输出为十进制数8。
举个例子——

#include<stdio.h>
int main()
{
    char c = 0x8;
    putchar('a');
    putchar(c);
    putchar('b');
    return 0;
}

最终输出结果为b,因为a被退格符删了。

延伸:C/C++规定,一个数如果要指明它采用八进制,必须在它前面加上一个0(数字0),如:123是十进制,但0123则表示采用八进制。这就是八进制数在C/C++中的表达方法。我们学过用一个转义符’\’加上一个特殊字母来表示某个字符的方法,如:’\n’表示换行(line),而’\t’表示Tab字符,’\”则表示单引号。另一种使用转义符的方法:转义符’\’后面接一个八进制数,用于表示ASCII码等于该值的字符。比如,问号(?)的ASCII值是63,那么我们可以把它转换为八进值:77,然后用 ‘\77’来表示’?’。由于是八进制,所以本应写成 ‘\077’,但因为C/C++规定不允许使用斜杠加10进制数来表示字符,所以这里的0可以不写。如果想使用十进制或者是十六进制给char赋值,则直接使用char c = 63(十进制)或者是char c = 0x3f(十六进制)。

大题

N位质数

N位质数
【问题描述】
给定一个整数N(2 <= N <= 8),生成所有的具有下列特性的特殊的N位质数:其前任意位都是质数。
例如,7331即是这样一个4位的质数,因为7、73和733也都是质数。
【输入形式】
标准输入上输入一个正整数N(2 <= N <= 8)。
【输出形式】
标准输出。 输出所有符合题意的N位质数,每个数占一行,且按升序排列。
【输入样例】
2
【输出样例】
23
29
31
37
53
59
71
73
79
【时间限制】
5s
【空间限制】
65536KB
【上传文件】
上传c语言源程序,文件名为nprime.c。

思路1(缺陷,输入为8位数的测试点超时):一开始,我使用的是埃拉托斯特尼筛法打出质数表,然后循环,依次判断每一个数从前往后是否都是质数。
果然,对于8位数的测试点超时了。时间一个是耗在了筛法打表上,另一个是耗在了循环遍历每一位数上。

#include<stdio.h>
#include<malloc.h>

#define bool int
#define true 1
#define false 0

int n;

void sieve(bool *prime, int num)
{
    int i, j, multi;
    prime[0] = prime[1] = false;
    for(i = 2; i * i <= num; ++i){    //小于等于,别忘了等于
        if(prime[i] == true){
            for(j = i * 2, multi = 2; j <= num; ++multi ){
                prime[j] = false;
                j = i * multi;
            }
        }
    }
}

bool judge(bool *prime, int num, int min)
{
    int div = min, prefix;
    while(div > 1){
        prefix = num / div;
        if(prime[prefix] == true){
            div /= 10;
        }
        else{
            return false;
        }
    }
    return true;
}

int main()
{
    int min = 1, max = 1, num, i;
    bool *prime;
    scanf("%d", &n);

    for(i = 0; i < n - 1; ++i){
        min *= 10;
    }
    max = min * 10 - 1;
    num = max + 11;

    prime = (bool *)malloc(sizeof(bool) * num);

    for(i = 0; i < num; ++i){
        prime[i] = true;
    }

    sieve(prime, num);

    for(i = min * 2; i <= max; ++i){
        if(prime[i] == true && judge(prime, i, min) == true){
            printf("%d\n", i);
        }
    }

    return 0;
}

其实这个题给的条件“限制的比较死”,要求数的前任意位都是质数,可想而知这些数并不多,所以可以直接去生成这些数。

思路2(所有测试点秒出):例如,2是质数,则判断以2开头的数是否是质数,判断到20,21,22都不是质数,则以20,21,22开头的数都不用考虑了。当判断到23为质数的时候,继续递归判断以23开头的数有哪些是质数。而递归结束和分支的条件就是n位数的上下界判断。
由于这样利用非常强的条件找到的质数个数本来就很少,所以判断一个数是否是质数可以直接用定义,此时如果用筛法不仅写着麻烦,运行时在筛法上浪费的时间也更多。

#include<stdio.h>
#include<malloc.h>

#define bool int
#define true 1
#define false 0

int n, min = 1, max = 1;

bool isPrime(int num)           //判断num是否是质数
{
    int i;
    if(num == 0 || num == 1){
        return false;
    }
    for(i = 2; i * i <= num; i++){
        if(num % i == 0){
            return false;
        }
    }
    return true;
}

void is_nPrime(int num)
{
    int i, number;
    for(i = 0; i <= 9; i++){
        number = num + i;
        if(isPrime(number) == true){        //如果当前数是质数
            if(number >= min && number <= max){ 
                printf("%d\n", number);
                is_nPrime(number * 10);     //递归判断以当前数为前缀的数是否是质数
            }
            else if(number < min){      
                is_nPrime(number * 10);
            }
            else if(number > max){      
                return;
            }
        }
    }
}

int main()
{
    int i;
    scanf("%d", &n);

    for(i = 0; i < n - 1; ++i){
        min *= 10;
    }
    max = min * 10 - 1;     //找出n位数的上下界

    is_nPrime(0);

    return 0;
}

泊松分布

泊松分布
【问题描述】
泊松分布是一种常用的离散型概率分布,数学期望为m的泊松分布的分布函数定义如下:
P(m,k)=(mkem)k!(k=0,1,2,3,)
对于给定的m和k(0 < m < 2000, 0 <= k < 2500),计算其概率,以科学格式输出,保留小数点后6位有效数字。
可以使用数学库函数,误差不超过0.000001。
【输入形式】
输入文件为当前目录下的poisson.in。 文件中包含两个数字,分别为m,k的值。
【输出形式】
输出文件为当前目录下的poisson.out。 文件中输出泊松分布的值,值以科学格式输出,保留小数点后6位有效数字。
【输入样例】
1 0
【输出样例】
3.678794e-01
【时间限制】
1s
【空间限制】
65536KB
【上传文件】
上传c语言源程序,文件名为poisson.c。

思路1(成功,但并不是最优选择):第一反应,k!肯定不能直接算,而分子上m的个数和k!里因子的个数相同,就将 mk k! 一块儿边除边算。实际上 em 也应该加入进来计算,才能保证更精确的结果,毕竟 em 单算的话用double表示其精度也够呛。
做到以上几点之后还是不够,仍有个别点过不了,说明精度还是不够,此时考虑用long double,之后输出为科学记数法的时候在强制转型为都double就行了。

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

long double m_k(int m, int k)
{
    long double result = 1.0;
    if(k == 0){
        return exp(-1 * m);
    }
    else if(m == 0){
        return 0.0;
    }
    else{
        if(m > k){
            result /= exp(m - k);
        }
        else if(m < k){
            result *= exp(k - m);
        }
        while(k > 0){
            result *= m * 1.0 / k / exp(1);
            k--;
        }
        return result;
    }
}

int main()
{
    int m, k;
    long double result = 1.0;

    freopen("poisson.in", "r", stdin);
    freopen("poisson.out", "w", stdout);

    scanf("%d%d", &m, &k);

    result *= m_k(m, k);
    printf("%e\n", (double)result);
    return 0;
}

思路2(最优思路,结合数学进行计算):思路1是纯编程思想,去模拟算式的计算过程。对于这个式子,更好的思路在于用数学方法化简,即对式子两边同时取ln,乘法变成了加法,会让式子非常简单!

母牛问题

母牛问题
【问题描述】
x年出生的母牛从第x+m年开始到第x+n年止(含, 1 < m < n)每年生小母牛一头,并在第x+p(n < p <= 60)年被淘汰。设第0年有刚出生的小母牛一头,求第k(k > 0)年存栏母牛多少头。
【输入形式】
从标准输入上顺序读入正整数m、n、p、k。
【输出形式】
以整数方式在标准输出上输出第k年的母牛存栏数s。
【输入样例】
3 5 7 12
【输出样例】
27
【时间限制】
1s
【空间限制】
65536KB
【上传文件】
上传c语言源程序,文件名为cow.c。

思路1(缺陷,最后一个测试点数组越界):这个题给人的第一感觉应该使用类似于斐波那契数列的递推式。然而由于我比较懒,觉得推出递推式太麻烦,于是就用代码模拟母牛的生产过程。
首先用一个一维数组表示所有母牛,下标代表第n头牛,相应的值代表这头牛的年龄,被淘汰的牛的年龄标记为-1,然后时间递增直到k,输出此时数组中牛的数量(值大于0的数字的个数)。
我的电脑,数组在堆里最大开到2000000,但是当牛数到50年左右就已经有一百多万了(类似于指数级增长),所以最后一个测试点理所当然的数组越界了。

#include<stdio.h>

#define N 2000000

int main()
{
    int age[N] = {0};
    int m, n, p, k, i, year, count, max, cow = 0;
    scanf("%d%d%d%d", &m, &n, &p, &k);

    age[0] = 0, max = count = 0;

    for(year = 1; year <= k; year++){
        for(i = 0; i <= count; i++){
            if(age[i] != -1){
                age[i]++;
                if(age[i] >= m && age[i] <= n){
                    max++;
                }
                else if(age[i] >= p){
                    age[i] = -1;
                }
            }
        }
        count = max;
    }

    for(i = 0; i <= max; i++){
        if(age[i] != -1){
            cow++;
        }
    }
    printf("%d\n", cow);
    return 0;
}

思路2(成功,优化效果惊人):对于同一年生出来的牛,他们的行为都是一样的,所以没必要每头牛都占用一个位置,事实上只用一个位置就可以表示同一年生出的所有的牛了。然后再开一个一维数组记录这一年新出生的牛的数量就行了。
最终的结果表明,优化之后的效果是惊人的——

lgl@pArch ~/Codes/C/Test $ ./cow          
3 5 7 50
cow number : 1174783
maximum array index used : 48

lgl@pArch ~/Codes/C/Test $ ./cow
3 5 7 60
cow number : 19552035
maximum array index used : 58

lgl@pArch ~/Codes/C/Test $ ./cow
3 5 7 100
cow number : 732494715
maximum array index used : 89

lgl@pArch ~/Codes/C/Test $ ./cow
3 5 7 5000
cow number : 1371126003
maximum array index used : 2315

第一行是输入的数据,最后一个数代表第k年,可以看出,第60年的时候牛的数量已经达到了接近二百万,而数组才用了58的长度;即使5000年,牛的数量达到了13.7亿,数组也才使用了2315个位置。因此,实际上数组大小完全没必要开到一百万。
至于另一种优化:将已淘汰的牛直接在数组中覆盖掉,而不是用-1标记,导致还占着地方,从上面的数组空间使用量我们可以看出这一优化方法实际上已经没有必要了。
而另一个我们能够大致看出的结论就是:牛的数量的确更像是在以接近指数的方式进行。
代码如下:

#include<stdio.h>

#define N 1000000

int main()
{
    int age[N] = {0}, num[N] = {0};
    int m, n, p, k, i, year, count, cow = 0, cow_birth;
    scanf("%d%d%d%d", &m, &n, &p, &k);

    age[0] = 0, num[0] = 1, count = 0;
        //count为当前用到的数组的最大下标
    for(year = 1; year <= k; year++){
        cow_birth = 0;
        for(i = 0; i <= count; i++){
            if(age[i] != -1){
                age[i]++;
                if(age[i] >= m && age[i] <= n){
                    cow_birth += num[i];
                }
                else if(age[i] >= p){
                    age[i] = -1;
                }
            }
        }
        if(cow_birth > 0){
            count++;
            num[count] = cow_birth;
        }
    }

    for(i = 0; i <= count; i++){
        if(age[i] != -1){
            cow += num[i];
        }
    }
    printf("%d\n", cow);
    return 0;
}

螺旋矩阵

螺旋矩阵
【问题描述】
输入一个自然数N(2≤N≤9),要求输出如下的螺旋矩阵,即边长为N*N,元素取值为1至N*N,1在左上角,呈顺时针方向依次放置各元素。
N=3时,相应的矩阵中每个数字位置如下图所示:
1 2 3
8 9 4
7 6 5
【输入形式】
从标准输入读取一个整数N。
【输出形式】
向标准输出输出一个N*N的螺旋矩阵,即边长为N*N,元素取值为1至N*N,1在左上角,呈顺时针方向依次放置各元素,每个数字占5个字符宽度,向右对齐,不足部分补以空格。在每一行末均输出一个回车符。
【输入样例】
3
【输出样例】
××××1××××2××××3
××××8××××9××××4
××××7××××6××××5
(这里×代表空格)
【时间限制】
1s
【空间限制】
65536KB
【上传文件】
上传c语言源程序,文件名为rotate.c。

#include<stdio.h>

#define N 10

int a[N][N];
int times;      //旋转矩阵旋转的圈数

void rotate(int n, int num)
{
    int i, j;
    int counter;

    if(n <= 0){     //处理边界情况
        return;
    }

    i = j = times - n / 2;

    if(n == 1){     //处理n为奇数时最中心的情况
        a[i][j] = num;
    }

    //每一圈分四个方向处理,每一次处理都用一个counter记录次数比较方便
    for(counter = n - 1; counter > 0; counter--, j++){
        a[i][j] = num++;
    }

    for(counter = n - 1; counter > 0; counter--, i++){
        a[i][j] = num++;
    }

    for(counter = n - 1; counter > 0; counter--, j--){
        a[i][j] = num++;
    }

    for(counter = n - 1; counter > 0; counter--, i--){
        a[i][j] = num++;
    }

    rotate(n - 2, num);    //递归进入下一圈
}

int main()
{
    int n, i, j;
    scanf("%d", &n);
    times = n / 2;
    rotate(n, 1);
    for(i = 0; i < n; i++){
        for(j = 0; j < n; j++){
            printf("%5d", a[i][j]);
        }
        putchar('\n');
    }
    return 0;
}

判断出栈序列

判断出栈序列
【问题描述】
对于一个栈,已知元素的进栈序列,判断一个由栈中所有元素组成的排列是否是可能的出栈序列。
比如,进栈序列为1 2 3 4,则可能的出栈序列有4 3 2 1,1 4 3 2等。而1 4 2 3就不是。
【输入形式】
从标准输入读取输入。 第一行是一个整数N(3≤N≤10),代表有N个元素,其进栈序列是1 2 3 …… N。 第二行是以空格分隔的1~N的数字的一个排列。
【输出形式】
向标准输出打印结果。 如果该排列是可能的出栈序列,则打印“YES”,否则打印“NO”。在行末要输出一个回车符。
【输入样例】
4
1 4 3 2
【输出样例】
YES
【时间限制】
1s
【空间限制】
65536KB
【上传文件】
上传c语言源程序,文件名为outstack.c。

#include<stdio.h>

#define N 11

int main()
{
    int n, i, j, k, top, in[N] = {0}, out[N] = {0}, stack[N] = {0};
    scanf("%d", &n);
    for(i = 0; i < n; i++){     //对输入和输出序列赋值
        scanf("%d", out + i);
        in[i] = i + 1;
    }

    i = j = k = 0;
    top = -1;
    while(i < n + 1&& j < n){   //进栈时i++,出栈时j++,先进栈后出栈,进栈完毕后不能结束,故i<n+1
        if(top != -1 && out[j] == stack[top]){  //out当前值和当前stack最后一个值相同,则可以出栈
            j++;
            top--;
            k++;
        }
        else{                                   //若不同,只能进栈
            stack[++top] = in[i];
            i++;
            k++;
        }
    }

    if(k == 2 * n){                             //k记录进出栈次数,进栈和出栈次数和为2n,则成功
        printf("YES\n");
    }
    else{
        printf("NO\n");
    }
    return 0;
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值