XTU-OJ 《C语言程序设计》 1223-Repeat One

题目描述

求由最小的一个N,N个数码1组成的数能被M整除? 比如M=3时,111能被3整除。M=2时,则不存在这样的N。

输入

第一行是一个整数K(K≤1,000),表示样例的个数。 以后每行一个整数M(1≤M≤1,000,000)

输出

每行输出一个样例的结果,如果不存在这样的N,输出0。

样例输入
 5
 1
 2
 3
 4
 999989
样例输出
 1
 0
 3
 0
 473670

解题思路: 这里我们设 X 是由 N个1 组成的数。

这题该怎么做呢,到底这个最小的N该怎么求,好像没有什么方法可以直接算出来。那怎么办?硬着头皮试吧。

如果有 X % M == 0,是不是就表明,X 可以被M整除,那么就存在这样的一个N,满足题目要求。所以就从最小的1开始,1 不行就试 11,11 不行就试 111,不行再 1111 、11111、 111····111。 应该总能试出来吧。

若是所有M取值都这样一个一个单纯的尝试,那是不是有点太呆了。我们能不能让它更简单一点?

  • 想想看,X 都是由 1 组成的,那它是不是永远不可能被 偶数 和 5的倍数 整除。所以对于这类的 M,不存在X 能被M整除 。这时的 N 一定为 0,这样我们一下就判断出了绝大多数的情况。

  • 然后思考--问题1:那到底要试多少轮?才能找到这样的 X 呢?并且除了上面两种情况外,还是存在不能整除X的M。不行的就是不行,试多少次都试不出来的。如果不限制尝试次数的话,就可能一直循环下去,造成死循环。这个问题我们先暂时放一边,等下再来思考。

  • 我们先思考--问题2:随着N的增大, X 会从 1 、11、111 增大到 111···111,X 会逐渐变得很大很大,大到计算机都表示不出来,那我们有没有什么方法可以防止出现这种情况呢?答案是有的——模运算

    • 什么是模运算?这里有模运算的三个基本运算式:

      (a + b) % p = (a % p + b % p) % p

      (a - b) % p = (a % p - b % p) % p

      (a * b) % p = (a % p * b % p) % p

      例: 111 % 3 = ( 11*10 + 1) % 3 = ( (11*10) % 3 + 1%3) % 3 = ( (11 % 3)*10 + 1) % 3

    • 思考题目,假设 M = 3。

      当 N = 2 时, 判断 11 % 3 == 0,如果是,则找到最小的N,不是则  N+1。

      下一轮N = 3 ,  此时判断  111 % 3 == 0,

      结合例题,有没有发现什么? 111 % 3 和 11 % 3 之间有什么联系? 上一轮的取模结果,可以保持为当前判断的因式
    • 这样我们每次取模(%)判断这个数能否能被M整除的同时,又对数值进行了放缩,让数值大小始终控制在一个范围,不会出现数值越界的情况。(这就是模运算的妙用)

  • 现在来解决问题1:

    我们对每轮都进行 X = X % M 判断; 因为若 X == 0,则已经能被M整除,输出当前N的数值,反之 再增加 X 的值。

    取模后X的范围一定是 [0,M-1],即 X%M 有 M 种取值情况。即便前M轮,X%M将所有的值都取一遍,那第M+1轮之后,模的取值一定会发生重复。模取值发生重复代表了什么? 一旦重复了,是不是就意味着会出现循环,之后的所有取模值,都是固定可预见的。

    所以如果在前M轮中,没有判断出 X % M == 0,那么之后X如何增加,都是无法得出 X 能被 M 整除这个结论的。因为之后都是对前M轮的重复判断。

    所以结合问题2的结论,只要判断前 M 次,就能够验证是否存在一个X,可以被M整除。

思路理清完毕,开始编程。

AC代码:

#include

int main()
{
    int K,N,M,X,flag;
    scanf("%d",&K);
    while ( K --)
    {
        scanf("%d",&M);
        X = 1;
        flag = 0;
        if ( M%2 == 0 || M%5 == 0)
            flag = 0;
        else
        {
            for (N = 1; N <= M; N ++)
            {
                X %= M;
                if (X == 0)       // 如果X == 0,表明可以被整除
                {
                    flag = 1;     //   flag(能被整除标志置1)
                    break;
                }
                X = X*10 + 1;     //  实现从1 ->  11 -> 111 增长的功能
            }
        }
        if (flag == 1)
            printf("%d\n",N);   // 输出最小X中1的数,即N的值
        else
            printf("0\n");
    }
}

你可能感兴趣的:(湘大OJ练习解析,算法,c语言,学习)