深入浅出程序设计竞赛学习笔记-第四章循环程序设计

深入浅出程序设计竞赛学习笔记-第四章循环程序设计

一、自增与自减操作

在这里插入图片描述

#include<iostream>
using namespace std;

int main()
{
    int s = 0,n;
    int i = 0;
    cin>>n;
    // for(int i = 1; i <= n; i++)
    // {
    //     s += i;

    // }

    //如果i的初始值是1,那么下面的语句就要变成s+=i++

    while(n--)
    {
        s += ++i;
    }
    cout<<s;
    return 0;
}

关于这道题目,while(n–)的意思是先判断n是否等于0,再将n减一,如果改成while(–n),就是将n先减一,再判断n是否等于0。在每一次循环中,先将i加一,再将i的值加入s中,如果i的初始值不是1,而是0,那么先加上i,再i++

二、浮点数产生死循环

案例:数列求和:计算0.1+0.2+0.3+…+(n-0.1),其中n是不大于100的整数。

分析:和上一个例子差不多,但是整数变成了浮点数,但是浮点数是有误差的,所以为了消除浮点数的误差,需要先将所有的数字全部乘以10然后累加。也就是从1累加到10n-1。

下面介绍错误的程序:

#include<iostream>
using namespace std;

int main(void)
{
    int n;
    double s = 0;
    cin>>n;

    for(double i = 0.1; i != n; i++)
    {
        s += i;
    }

    cout<<s;
    return 0;

}

运行程序,发现程序一直在运行,没有停止,造成这个bug的原因是:浮点数的精度误差。由于0.1这个数字无法被精确的表示成二进制数字,而且现在又对i进行了大量的运算,所以会产生误差。100个0.1相加产生的误差导致结果越偏越多。

这也是为什么浮点数比较是否相等时不可以使用==而是要检查差距是否小于可以接受的误差范围内。

像这样无论怎么样都达不到循环终止条件的循环,称之为死循环

同样的,因为浮点数误差,这里的判断条件不可以写成i < n,否则会引发死循环,但是可能导致延迟跳出循环。

正确的程序如下:

#include<iostream>
using namespace std;

int main(void)
{
    int n;
    double s = 0;
    cin>>n;

    for(int i = 1; i != 10 * n - 1; i++)
    {
        s += i;
    }

    s = s / 10;

    cout<<s;

    return 0;
}

三、质数判断

在这里插入图片描述

分析:设一个循环变量i从2开始,依次判断它是不是质数,如果他不是质数,就重新进行下一轮循环;如果他是质数,就判断加入它之后会不会超重,如果超重了,那么更大的质数也一定会超重,于是就结束循环;如果没有超重,就输出i,同时将i加入背包内。

重点在与判断质数:

质数的定义:如果一个大于1整数仅能被1和自身整除,他就是一个质数,否则就是合数(1既不是质数也不是合数)

判断方法:i从2开始进行循环,一个数一个数进行枚举j.如果J可以被i整除,如果能被整除,说明他不是质数,立即跳出循环,如果到最后都不能被整除,说明他就是质数。

但是,我们不需要从1一直枚举到i-1,只需要枚举到根号i即可。因为如果i存在不是1或者自身的约数,那么就会成对的出现,(或者就是i的算术平方根)其中一个不大于根号i,另外一个不小于根号i,所以只需要枚举到小于或者等于根号i的数字即可,尽可能再不影响结果的情况下减少循环次数。

程序如下:

#include<iostream>
using namespace std;

int main()
{
    int load = 0;//口袋的重量 
    int L;
    int count = 0;
    cin>>L;  
    for(int i = 2; ; i++)
    {
        int is_prime = 1;//首先假设他就是质数
        //判断是不是质数
        for(int j = 2; j * j <= i; j++)
        {
            if(i % j == 0)
            {
                is_prime = 0;
                break;
            }
        }

        if(is_prime == 0)
        {
            // 如果i不是质数 进行下一次循环
            continue;
        }
        
        if(i + load > L)
        {
            break;//跳出外层循环 直接结束 超重
        }

        cout<<i<<endl;
        count++;
        load = load + i;

    }

    cout<<count;
    return 0;
}

说明以下几点:

  • 程序中没有专门判断j <= 根号i,而是判断j * j <= i 这么做的理由是计算机进行乘法运算要好于除法运算。

  • break的作用是跳出一层循环,continue的作用是跳过本次循环中未执行的语句,重新开始一轮新的循环。

四、回文质数

在这里插入图片描述

题目解析:

关于回文质数的定义:一个数既是质数又是回文数 比如151

分析:最容易想到的方式是外层循环从a开始一个一个进行枚举直到b为止,先判断这个数字是否是回文数如果是回文数在判断这个数是不是质数,如果这个数是回文质数 输出即可。

关于判断回文数:首先判断一个数字的位数,然后分离各位数字,判断首尾是否相等。

但是由于时间限制,枚举的效率十分低下,所以进行改进:考虑一个回文数第一个数字和最后一个数字相等,第二个数字等于倒数第二个数字,所以只要枚举回文数的前一半,后面一半可以根据前面一半构造出来,例如枚举到四位数字1234,它可以构造出来回文数1234321或者12344321,这样一亿以内的回文数最多只需要枚举所有的四位数就可以构造出来,枚举的数量也减少到10000以内,然后在判断是不是质数,如果是,则输出。

首先判断不小于5且在两位数以内的回文质数只有5、7、11。并且其他两位数都不是质数,同时可以证明所有的偶数位数的回文数都是11的倍数,所以不是质数,所以不存在8 6 4 位的回文质数,只存在3 5 7 的回文质数。

如果枚举出的回文数没有达到下限a,继续枚举,如果枚举出来的数字大于b,那么后面的数字还是大于b,直接退出程序即可。

得到枚举的回文数num之后,就要判断这个数是不是质数,可以从3开始枚举(不用从2开始枚举 因为这里的回文数一定是奇数),如果是质数就输出。

代码如下:

#include<iostream>
using namespace std;


int main()
{
    //首先判断 不小于5 且在两位数以内的回文质数,最多到11
    //其他两位数的回文数都是11的倍数 所以不是质数
    int a,b;
    cin>>a>>b;

    if(a <= 5 && b >= 5) cout << 5 << endl;
    if(a <= 7 && b >= 7) cout << 7 << endl;
    if(a <= 11 && b >= 11) cout << 11 << endl;

    //可以证明所有偶数位数的回文数都是11的倍数,所以不是质数
    //所以不存在八位数的回文质数 也不存在 6 4 位的回文质数
    //只存在 3 5 7 位的回文质数

    //所有的偶数一定不是质数  所以外层循环直接都是奇数
    for(int d1 = 1; d1 <= 9; d1 += 2)
    {
        //三位数 内层循环从0 - 9依次判断
        for(int d2 = 0; d2 <= 9; d2++)
        {
            //构造出来一个回文数 101
            int num = 100 * d1 + 10 * d2 + d1;

            //判断是否在ab范围之内
            if(num < a)
            {
                continue;
            }

            if(num > b)
            {
                return 0;
            }

            //判断是否为质数
            int flag = 1;//首先默认是质数
            for(int j = 2; j * j <= num; j++)
            {
                if(num % j == 0)
                {
                    flag = 0;
                    break;
                }
            }

            if(flag == 1)
            {
                cout<<num<<endl;
            }
        }

    }


    //五位数
    for(int d1 = 1; d1 <= 9; d1+=2)
    {
        for(int d2 = 0; d2 <= 9; d2++)
        {
            for(int d3 = 0; d3 <= 9; d3++)
            {
                //比如 12321
                int num = d1 * 10000 + d2 * 1000 + d3 * 100 + d2 * 10 + d1;
                if(num < a)
                {
                    continue;
                }
                if(num > b)
                {
                    return 0;
                }

                int flag = 1;
                for(int j = 2; j * j <= num; j++)
                {
                    if(num % j == 0)
                    {
                        flag = 0;
                        break;
                    }
                }

                if(flag == 1)
                {
                    cout<<num<<endl;
                }
            }
        }
    }

    for(int d1 = 1; d1 <= 9; d1 += 2)
    {
        for(int d2 = 0; d2 <= 9; d2++)
        {
            for(int d3 = 0; d3 <= 9;d3++)
            {
                for(int d4 = 0; d4 <= 9; d4++)
                {
                    //1234321
                    int num = d1 * 1000000 + d2 * 100000 + d3 * 10000 + d4 * 1000 + d3 * 100 + d2 * 10 + d1;
                    if(num < a)continue;
                    if(num > b)return 0;

                    int flag = 1;
                    for(int j = 2; j * j <= num; j++)
                    {
                        if(num % j == 0)
                        {
                            flag = 0;
                            break;
                        }
                    }

                    if(flag == 1)
                    {
                        cout<<num<<endl;
                    }

                }
            }
        }
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

少写代码少看论文多多睡觉

求打赏,求关注,求点赞

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值