《妙趣横生的算法》(C语言实现)- 第6章 数学趣题(二)

【6.1】连续整数固定和问题:找出任意输入的整数n的全部的连续整数固定和。
题目分析: 至少要找出两个连续整数的固定和,一个整数的话就是本身了呢。那如何确定这些连续整数呢?想明白了,第一个整数设为a,第二个整数是a+1,…,假设有m个连续整数,那么第m个整数是a+(m-1),这m个整数的和是ma+m*(m-1)/2。

// 连续整数固定和问题 2023年12月20日 16点45分-17点01分
# include <stdio.h>
# include <math.h>
int main()
{
    int n;
    printf("please input n:");
    scanf("%d", & n); // input
    for (int a = 1; a < n; ++a) {
        double m1 = (1-2*a+sqrt((2*a-1)*(2*a-1)-4*(-2*n))) / 2.0;
//        double m2 = (1-2*a-sqrt((2*a-1)*(2*a-1)-4*(-2*n))) / 2.0;
        if (m1 == (int)m1) {
//            printf("m1 = %f, m2 = %f\n", m1, m2);
            printf("%d=%d", n, a); // output
            for (int i = 1; i < m1; ++i) {
                printf("+%d", a+i);
            }
            printf("\n");
        }
    }
    return 0;
} 
// 书上代码
# include <stdio.h>
void cntnsIntSum(int n)
{
    int i, sum = 0, j;
    for (int i = 1; i < n; ++i) { // 控制起点的选择,从1到n-1 
        j = i - 1;
        while (sum < n) { // 从起点向后顺序累加 
            ++j;
            sum += j;
        }
        if (sum == n) {
            printf("%d+...+%d = %d\n", i, j, n);
        }
        sum = 0;
    }
} 

int main()
{
    int n;
    printf("Please input a integer\n");
    scanf("%d", & n);
    cntnsIntSum(n);
    return 0;
}

【6.2】表示成两个数的平方和:找出所有满足X的平方加Y的平方等于N的正整数对X和Y。
题目分析: 穷举法。双层循环就可。

// 表示成两个数的平方和 2023年12月20日17点10分-17点19分
# include <stdio.h>
# include <math.h>
int main()
{
    int n;
    printf("Please input n:"); // input
    scanf("%d", & n);
    for (int x = 1; x <= sqrt(n); ++x) {
        for (int y = x; y <= sqrt(n - x*x); ++y) {
            if (x*x + y*y == n) {
                printf("%d = %d^2 + %d^2\n", n, x, y); // output
            }
        }
    }
    return 0;
} 

总结: 书上的代码是将双重for循环放到一个函数里,然后调用这个函数。
【6.3】具有特殊性质的数:有这样一个4位数abcd等于 (ab+cd)的平方,其中ab≠cd,求这个4位数。
题目分析:穷举法。四层循环即可。

// 具有特殊性质的数 2023年12月20日17点22分-17点26分 
# include <stdio.h>
void GetABCD(void) {
    for (int a = 1; a < 10; ++a) { // ab是2位数,十位不为0 
        for (int b = 0; b < 10; ++b) {
            for (int c = 1; c < 10; ++c) { // cd是2位数,十位不为0,这里书上不一样,不过我觉得我是正确的,哈哈哈~
                for (int d = 0; d < 10; ++d) {
                    if (a*1000+b*100+c*10+d == (a*10+b+c*10+d)*(a*10+b+c*10+d)) {
                        printf("abcd = %d%d%d%d\n", a, b, c, d);
                    }
                }
            }
        }
    }
}

int main()
{
    GetABCD();
    return 0;
} 

【6.4】验证角谷猜想:编写程序加以验证。
题目分析:按部就班。循环即可。

// 验证角谷猜想 2023年12月20日17点29分-17点34分 
# include <stdio.h>
void conjecture(int num)
{
    printf("%d", num);
    while (num != 1) {
        if (num % 2) // 奇数 
            num = 3 * num + 1;
        else // 偶数 
            num /= 2;
        printf("->%d", num);
    }
}

int main()
{
    int n;
    printf("Please input n:");
    scanf("%d", & n);
    conjecture(n);
    return 0;
} 

【6.5】验证四方定理:所有自然数最多只要4个数的平方和就可以表示,编写程序加以验证。
题目分析:怎么表示最多只要4个数,这4个数是什么。如果一个自然数a本身是平方数,那么直接开方就得到了这个数m。如果一个自然数a本身不是平方数,那么开方之后取整得到m,将自然数a减去m*m再进行判断是否要循环。

// 验证四方定理 2023年12月21日09点11分-09点23分 
// 这种方法不可行,n=156时输出5
# include <stdio.h>
# include <math.h>
void The_Quartet_Theroem(int num)
{
    int cnt = 0;
    while (1) {
        double r = sqrt(num);
        printf("r = %d, ", (int)r);
        ++cnt; // 计数
        num -= ((int)r * (int)r);
        if (0 == num) {
            printf("cnt = %d\n", cnt);
            break;
        }
    }
}
int main()
{
    int n;
    printf("Please input n:"); // input
    scanf("%d", & n);
    The_Quartet_Theroem(n);
    return 0;
}
// 书上代码分情况讨论,至多有4个整数,分为4种情况
// 验证四方定理 2023年12月21日09点27分-09点39分 
# include <stdio.h>
# include <math.h>

int mode_1(int num) 
{
    if ((int)sqrt(num) * (int)sqrt(num) == num) { // 这个数本身就是平方数 
        printf("%d*%d=%d\n", (int)sqrt(num), (int)sqrt(num), num);
        return 1;
    } else {
        return 0;
    }
}
int mode_2(int num)
{
    for (int x = 1; x < sqrt(num); ++x) { // 这个数是两个平方数之和 
        for (int y = x; y < sqrt(num); ++y) { // 这两个数可以相等,比如8=2^2+2^2 
            if (x*x + y*y == num) {
                printf("%d^2+%d^2=%d\n", x, y, num);
                return 1;
            }
        }
    }
    return 0;
}
int mode_3(int num)
{
    for (int x = 1; x < sqrt(num); ++x) { // 这个数是三个平方数之和 
        for (int y = x; y < sqrt(num); ++y) { // 这三个数可以相等,比如12=2^2+2^2+2^2 
            for (int z = y; z < sqrt(num); ++z) {
                if (x*x + y*y + z*z == num) {
                    printf("%d^2+%d^2+%d^2=%d\n", x, y, z, num);
                    return 1;
                }
            }
        }
    }
    return 0;
}
int mode_4(int num)
{
    for (int x = 1; x < sqrt(num); ++x) { // 这个数是四个平方数之和 
        for (int y = x; y < sqrt(num); ++y) { 
            for (int z = y; z < sqrt(num); ++z) {
                for (int t = z; t < sqrt(num); ++t) {
                    if (x*x + y*y + z*z + t*t == num) {
                        printf("%d^2+%d^2+%d^2+%d^2=%d\n", x, y, z, t, num);
                        return 1;
                    }
                }
            }
        }
    }
    return 0;
}
void proveFourSquares(int num)
{
    if (mode_1(num))
        printf("It has verfied Four Squares");
    else if (mode_2(num))
        printf("It has verfied Four Squares");
    else if (mode_3(num))
        printf("It has verfied Four Squares");
    else if (mode_4(num))
        printf("It has verfied Four Squares");
}
int main()
{
    int n;
    printf("Please input a natural number\n");
    scanf("%d", & n);
    printf("------Step of Verification------\n");
    proveFourSquares(n);
    return 0;
}

【6.6】递归法寻找最小值:编写程序要求从一个整数序列中找出最小的元素,并用递归的方法实现。
题目分析:顺序查找肯定是最容易理解的,但是如何使用递归呢?这个整数序列无序,哪个地方使用递归呢?

// 递归法求最小值 2023年12月21日09点50分-10点02分
# include <stdio.h>
int getMin(int array[], int n)
{
    int val1, val2, val3;
    if (n == 1) { // 只有一个元素 
        return array[0];
    } else if (n % 2 == 0) { // 序列长度为2的倍数 
        val1 = getMin(array, n/2); // 得到前半个序列中的最小元素val1
        val2 = getMin(array+n/2, n/2); // 得到后半个序列中的最小元素val2
        if (val1 < val2) // val1 < val2,返回val1 
            return val1;
        else return val2; // val1 >= val2,返回val2 
    } else { // 序列长度为奇数 
        val1 = getMin(array, n/2); // 得到中位元素之前的子序列中的最小值
        val2 = getMin(array+n/2+1, n/2); // 得到中位元素之后的子序列中的最小值
        val3 = array[n/2];
        if (val1 < val2) {
            if (val1 < val3)
                return val1;
            else
                return val3;
        } else {
            if (val2 < val3)
                return val2;
            else
                return val3;
        }
    } 
}
int main()
{
    int n;
    printf("Please input a natural number:");
    scanf("%d", & n);
    int arr[n];
    printf("Please input the elements of this array:");
    for (int i = 0; i < n; ++i) {
        scanf("%d", & arr[i]);
    }
    int val = getMin(arr, n);
    printf("The minimum element of this array is %d\n", val);
    return 0;
}

总结: 书上分析是把这个整数序列分为两部分,从左序列找最小,再从右序列中找最小,重复寻找。
【6.7】寻找同构数:编写程序找出1000以内的同构数。
题目分析:穷举法。循环即可。

// 寻找同构数 2023年12月21日10点22分-10点26分 
# include <stdio.h>
void findNum(void)
{
    for (int i = 1; i < 1000; ++i) {
        int dig = 0, k = 10;
        if (i < 10)
            dig = 1;
        else if (i < 100) {
            dig = 2;
            k = 100;
        } else {
            dig = 3;
            k = 1000; 
        }
        //for (k = 10; k < 1000; k *= 10) // 书上代码更简洁
        //    if (i / k == 0)
        //        break;
        if (i*i%k == i) {
            printf("%d ", i);
        }
    }
} 
int main()
{
    printf("The Isomorphic numbers bellow 1000 are\n");
    findNum();
    return 0;
}

【6.8】验证尼科彻斯定理:编写程序验证任何一个整数的立方都可以表示成一串连续奇数的和。
题目分析:穷举法。循环即可。

// 验证尼科彻斯定理 2023年12月21日10点37分-10点43分 
# include <stdio.h>
void Nicoqish(int num)
{
    for (int i = 1; i < num*num*num; ++i) { // i为起点
        int sum = 0;
        for (int j = i; j < num*num*num; j += 2) { // j控制从i向后顺次累加 
            sum += j;
            if (sum == num*num*num) { // 如果奇数序列的和等于num*num*num 
                printf("%d = %d", num*num*num, i); // 输出结果,程序返回 
                for (int k = i+2; k <= j; k += 2) {
                    printf("+%d", k);
                }
                return ;
            } else if (sum > num*num*num) { // 如果奇数序列的和大于num*num*num,跳出本次循环 
                break;
            }
        } 
    }
    printf("Error\n");
}
int main()
{
    int n;
    printf("Please input a integer to verify Nicoqish Law\n");
    scanf("%d", & n);
    Nicoqish(n);
    return 0;
} 

【6.9】三重回文数字:编写程序找出11~999之间的所有的三重回文数字。
题目分析:穷举法。循环即可。

// 三重回文数字 2023年12月21日10点46分-10点50分 
# include <stdio.h>
long reverse(long num) // 求逆序数 
{
    long m = 0;
    while (num) {
        m = m * 10 + num % 10;
        num /= 10;
    } 
    return m;
} 
int ispalindrome(long num) // 判断是否是回文数字 
{
    if (reverse(num) == num)
        return 1;
    else
        return 0;
}
void func()
{
    for (long i = 11; i < 1000; ++i) {
        if (ispalindrome(i) && ispalindrome(i*i) && ispalindrome(i*i*i)) {
            printf("%ld ", i);
        }
    }
}
int main()
{
    func();
    return 0;
}

【6.10】马克思手稿中的数学题:30个人包括男女小孩,分别花3先令、2先令、1先令,吃饭花了50先令。
题目分析:穷举法。循环即可。

// 马克思手稿中的数学题 2023年12月21日10点55分-10点58分 
# include <stdio.h>
void Marx(void)
{
    for (int men = 1; men <= 50 / 3; ++men) {
        for (int women = 1; women <= 50 / 2; ++women) {
            for (int children = 1; children <= 50; ++children) {
                if (men+women+children == 30 && 3*men+2*women+children == 50) { // 如果符合条件 
                    printf("men = %d, women = %d, children = %d\n", men, women, children); // 输出结果 
                }
            }
        }
    }
}
int main()
{
    printf("The solutions of Marx's topic\n");
    Marx();
    return 0;
}

【6.11】渔夫捕鱼问题:编个程序算出5个渔夫至少合伙捕了多少条鱼。
题目分析:穷举法。循环即可。

// 渔夫捕鱼问题 2023年12月21日11点03分-11点15分 
# include <stdio.h>
int getfish(int init, int n)
{
    int s = init;
    while (n) {
        s = 5 * s + 1;
        --n;
    }
    return s;
}
int main()
{
    printf("Fish which were gotten by fishers at least are %d", getfish(6, 4));
    return 0;
}

【6.12】寻找假币:国王赏赐给大臣30枚金币,其中有一枚假币,用最少次数找到这枚假币。
题目分析:两两比较肯定效率很低。分为两部分,分别比较。类似于折半查找。

// 寻找假币 2023年12月21日11点28分-11点40分 
# include <stdio.h>
int getFalseCoin(int coin[], int low, int high)
{
    int sum1 = 0, sum2 = 0, sum3 = 0;
    if (low + 1 == high) {
        if (coin[low] < coin[high]) // 返回假币的编号 
            return low + 1;
        else
            return high + 1;
    }
    if ((high-low+1) % 2 == 0) { // 偶数个硬币 
        for (int i = low; i <= low+(high-low)/2; ++i) {
            sum1 += coin[i]; // 前半段和 
        }
        for (int i = low+(high-low)/2+1; i <= high; ++i) {
            sum2 += coin[i]; // 后半段和 
        }
        if (sum1 < sum2)
            return getFalseCoin(coin, low, low+(high-low)/2);
        else if (sum1 > sum2)
            return getFalseCoin(coin, low+(high-low)/2+1, high);
    }
    if ((high-low+1) % 2 != 0) { // 奇数个硬币 
        for (int i = low; i <= low+(high-low)/2; ++i) {
            sum1 += coin[i]; // 前半段和 
        }
        for (int i = low+(high-low)/2; i <= high; ++i) {
            sum2 += coin[i]; // 后半段和 
        }
        sum3 = coin[low+(high-low)/2]; 
        if (sum1 < sum2)
            return getFalseCoin(coin, low, low+(high-low)/2-1);
        if (sum1 > sum2)
            return getFalseCoin(coin, low+(high-low)/2+1, high);
        if (sum1 + sum3 == sum2 + sum3)
            return low + (high-low)/2 + 1;
    }
}
int main()
{
    int coin[30] = {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2};
    printf("The %dth coin is false\n", getFalseCoin(coin, 0, 29));
    return 0;
} 

【6.13】计算组合数:编写程序计算组合数。
题目分析:阶乘计算。算就行了。

// 计算组合数 2023年12月21日11点41分-11点45分
# include <stdio.h>
long long int fact(int num)
{
    long long p = 1;
    for (int i = 1; i <= num; ++i)
        p *= i;
    return p;
}
long long int func(int m, int n)
{
    return fact(m) / (fact(n) * fact(m - n));
}
int main()
{
    int m, n;
    printf("Please input two integers:");
    scanf("%d%d", & m, & n);
    printf("The result is %lld", func(m, n));
    return 0;
} 
// 计算组合数 2023年12月21日11点46分-11点48分
// 书上代码,使用递归
# include <stdio.h>
int cnr(int m, int n)
{
    if (m == n || n == 0)
        return 1;
    else
        return cnr(m-1, n) + cnr(m-1, n-1);
} 
int main()
{
    int m, n;
    printf("Please input m and n for C(m, n)\n");
    scanf("%d%d", & m, & n);
    printf("C(%d, %d) = %d", m, n, cnr(m, n));
    return 0;
}

【6.14】递归法求幂:编写一个递归算法,计算m的n次方。
题目分析:n=0时,结果为1;n>0时,结果为m*(m的n-1次方)。算就行了。

// 递归法求幂 2023年12月21日14点38分-14点41分
# include <stdio.h>
int func(int p, int q)
{
    if (q == 0)
        return 1;
    else
        return p * func(p, q - 1);
}
int main()
{
    int m, n;
    printf("Please input two integers:");
    scanf("%d%d", & m, & n);
    printf("The result is %d", func(m, n));
    return 0;
} 
// 递归法求幂 2023年12月21日14点47分-14点53分 
# include <stdio.h> // 书上代码
unsigned long int myPow(int m, int n)
{
    if (n == 0)
        return 1;
    if (n == 1)
        return m;
    if (n % 2 == 0) {
        unsigned long int tmp = myPow(m, n / 2);
        return tmp * tmp;
    } else {
        return m * myPow(m, n - 1);
    }
}
int main()
{
    int m, n;
    printf("Please input the bottom number:"); // 输入底数m
    scanf("%d", & m); 
    printf("Please input the exponent number:"); // 输入指数n
    scanf("%d", & n);
    printf("The result of power(%d, %d) is %lu", m, n, myPow(m, n));
    return 0;
} 

总结: 递归法求幂比循环连乘效率要高。采用临时变量保存返回值,体现了算法的优势,提高算法的效率。注意数据类型。
【6.15】汉诺Hanoi塔:编写程序实现移动的步骤。
题目分析:经典问题,递归如何实现呢?n个盘子可以看成(n-1)个盘子和1个盘子组成的。递归结束条件是什么,递归部分怎么写?还是想不明白。

// 汉诺塔 2023年12月21日15点01分-15点20分 
# include <stdio.h> // 书上代码 
void move(int n, char x, char y, char z)
{
    if (n == 1) // 递归结束条件,只有一个盘子,无需借助y,直接显示移动过程x到z 
        printf("%c-->%c\n", x, z);
    else {
        move(n - 1, x, z, y); // 先将n-1个盘子从x借助z移到y 
        printf("%c-->%c\n", x, z); // 然后将第n个盘子直接移到z,也就是显示移动过程x到z 
        move(n - 1, y, x, z); // 最后将y上的n-1个盘子通过x移到z 
    }
} 
int main()
{
    int n;
    printf("Input disks number:");
    scanf("%d", & n);
    printf("The step to move %d disks:\n", n);
    move(n, 'A', 'B', 'C');
    return 0;
}

【6.16】选美比赛:在现场按照选手的出场顺序宣布最后得分和最后名次。
题目分析:排序问题。如何排序。

// 选美比赛 2023年12月21日15点32分-15点44分 
# include <stdio.h>
struct player{
    int num;
    int score;
    int rand;
};
void sortScore(struct player psn[], int n) // 冒泡排序 
{
    for (int i = 0; i < n-1; ++i) {
        for (int j = 0; j < n-1-i; ++j) {
            if (psn[j].score > psn[j+1].score) {
                struct player tmp = psn[j];
                psn[j] = psn[j+1];
                psn[j+1] = tmp;
            }
        }
    }
}
void setRand(struct player psn[], int n) 
{
    int k = 1;
    psn[0].rand = k;    
    for (int i = 1; i < n; ++i) {
        if (psn[i].score != psn[i-1].score) {
            psn[i].rand = ++k;
        } else {
            psn[i].rand = k;
        }
    }
}
void sortNum(struct player psn[], int n)
{
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n-1-i; ++j) {
            if (psn[j].num > psn[j+1].num) {
                struct player tmp = psn[j];
                psn[j] = psn[j+1];
                psn[j+1] = tmp;
            }
        }
    }
}
void sortRand(struct player psn[], int n)
{
    sortScore(psn, n); // 以分数为关键字排序
    setRand(psn, n); // 按照分数排名次
    sortNum(psn, n); // 按照序号重新排序 
}
int main()
{
    struct player psn[7] = {{1,5,0}, {2,3,0}, {3,4,0}, {4,7,0}, {5,3,0}, {6,5,0}, {7,6,0}};
    sortRand(psn, 7);
    printf("num  score rand\n");
    for (int i = 0; i < 7; ++i)
        printf("%d%6d%6d\n", psn[i].num, psn[i].score, psn[i].rand);
    return 0;
}

总结: 这一章中的题目做起来不太难,容易理解并且实现。用到了穷举法和递归法解决问题,要继续加油哦!还是要多加练习哦!

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值