PTA| L1-046 整除光棍 (JAVA代码)

先上题目

L1-046 整除光棍 (20 分)

这里所谓的“光棍”,并不是指单身汪啦~ 说的是全部由1组成的数字,比如1、11、111、1111等。传说任何一个光棍都能被一个不以5结尾的奇数整除。比如,111111就可以被13整除。 现在,你的程序要读入一个整数x,这个整数一定是奇数并且不以5结尾。然后,经过计算,输出两个数字:第一个数字s,表示x乘以s是一个光棍,第二个数字n是这个光棍的位数。这样的解当然不是唯一的,题目要求你输出最小的解。

提示:一个显然的办法是逐渐增加光棍的位数,直到可以整除x为止。但难点在于,s可能是个非常大的数 —— 比如,程序输入31,那么就输出3584229390681和15,因为31乘以3584229390681的结果是111111111111111,一共15个1。

输入格式:

输入在一行中给出一个不以5结尾的正奇数x(<1000)。

输出格式:

在一行中输出相应的最小的sn,其间以1个空格分隔。

输入样例:

31

输出样例:

3584229390681 15

 一看题目样例 就觉得数据有点大

用普通暴力也确实没有全部正确

看了大佬们写的博客 大多都是C++模拟除法

但是 已经学了JAVA大整数的我就不一样了(其实就是懒得模拟)

长时间不碰JAVA也快忘记了……

还差点忘了BigInteger的比较用compare

现在上代码

import java.math.*;
import java.io.*;
import java.math.BigInteger;
import java.util.*;

public class Main {
	public static void main(String[] args) {
		Scanner cin = new Scanner(System.in);
		BigInteger zero = BigInteger.valueOf(0);
		BigInteger one = BigInteger.valueOf(1);
		BigInteger ten = BigInteger.valueOf(10);
		BigInteger m = BigInteger.valueOf(1);
	    BigInteger n = BigInteger.valueOf(1);
	    BigInteger x;
		x = cin.nextBigInteger();
		while (m.mod(x).compareTo(zero)>0)
		{
			m = m.multiply(ten);
			m = m.add(one);
			n=n.add(one);
		}
		BigInteger s=m.divide(x);
		System.out.print(s);
		System.out.print(" ");
		System.out.print(n);
	}
}

队友用Python做了一次 但是有点小错误(非零返回)

等她做出来了 也会更新的~

<think>我们正在处理用户关于翁恺C语言PTA习题7-3“整除光棍”的查询。根据引用[1],题目要求是通过增加光棍的位数(即由1组成的数字,如1, 11, 111等)直到能够整除给定的整数x,并输出结果。难点在于当x较大时,光棍数可能非常大,超出普通整型的范围,因此不能直接使用普通整数类型来存储。 解题思路: 1. 模拟除法过程:我们不需要真正构造出一个巨大的由1组成的数,而是可以通过模拟除法竖式的过程,每次用当前的余数乘以10加1(相当于在末尾添一个1)作为新的被除数,再除以x,直到余数为0。 2. 记录位数:同时记录进行了多少次这样的操作(即添加了多少个1),从而得到光棍数的位数。 3. 输出商:在模拟除法过程中,每次得到的商就是结果数字的每一位,可以逐位输出。但要注意,可能一开始会有一些0(即不够除时商0),但根据题目要求,我们输出的是整数,所以前面的0不能输出,需要用一个标志位来标记是否已经开始输出非零数字(即第一个非零商出现后,后面的0都要输出)。 具体步骤: - 输入整数x(假设x为正整数,且不为0,题目中x为非负整数,但实际x>=1)。 - 初始化余数r为0,计数器count为0(记录位数)。 - 使用循环,直到余数为0: a. 将余数更新为 r = r * 10 + 1 b. 计数器count加1 c. 计算商 s = r / x d. 计算余数 r = r % x e. 如果此时s不为0,或者我们已经开始输出非零数字(用一个标志变量如started),则输出s;否则,如果s为0且还没有开始输出,则跳过(不输出前导0)。但是注意,有可能整个商就是0,但这种情况不会发生,因为光棍数至少是1,而x>=1,所以商至少为1?不过题目中x可能大于1,所以一开始商可能是0(例如x>1时,第一个1除以x商0),所以我们需要处理前导0。 - 循环结束后,输出空格和count(即光棍数的位数)。 但是,还有一种情况:如果x=1,那么第一个1除以1就结束了,商是1,位数是1。 注意:题目要求输入的是一个正整数x,且x不以5结尾(这样保证有解),所以我们不需要考虑无解的情况。 代码示例: ```c #include <stdio.h> int main() { int x; scanf("%d", &x); int r = 0; // 余数 int count = 0; // 记录位数 int started = 0; // 标记是否已经开始输出商的非零部分(即遇到第一个非0商后,后面即使是0也要输出) // 如果x为0,题目没有给出,但一般x>=1,所以不考虑0 // 开始模拟除法 // 注意:我们需要一直做,直到余数为0 // 但是,有可能一开始余数就是0?不可能,因为初始r=0,然后r=0*10+1=1,所以不会一开始就为0。 // 另外,题目保证有解,所以循环一定会结束(因为光棍数位数有限,一定能整除,虽然位数可能很大) while (1) { r = r * 10 + 1; // 相当于在余数后面添一个1 count++; // 位数加1 int s = r / x; // 商 r = r % x; // 新的余数 // 当s不为0,或者我们已经开始输出(即前导0已经跳过)时,输出s // 或者当s为0,但是我们已经开始输出了,那么也要输出这个0(因为它在中间) if (s != 0 || started) { printf("%d", s); started = 1; // 一旦开始输出,就将标记置为1 } // 如果余数为0,说明已经整除,结束 if (r == 0) { break; } } // 如果整个过程中一个数字都没有输出(即count>0但s始终为0)?但题目保证有解,且x>=1,所以至少有一位非0商(当x=1时,第一次s=1;当x>1时,当位数足够多时,商总会出现非0,而且前面我们跳过了前导0,所以当x>1时,一开始的0被跳过,后面必然会有非0商出现,所以started一定会被置为1,所以不会出现没有输出的情况) // 但是,如果x=1,那么第一次就会输出1,然后余数0,退出循环。 // 输出空格和位数 printf(" %d\n", count); return 0; } ``` 但是,上面代码有一个问题:当x=1时,输出是“1 1”,正确。但是,如果x=3,那么: 第一步:r=1 -> s=0, r=1 -> 不输出,count=1 第二步:r=1*10+1=11 -> s=3(11/3=3),r=2 -> 输出3,started=1,count=2 第三步:r=2*10+1=21 -> s=7 (21/3=7), r=0 -> 输出7,然后退出。所以输出“37 3”,而实际3*37=111,所以商是37,位数3位,正确。 但是,我们注意到,第一次的商0被跳过了,所以输出37而不是037,符合要求。 然而,考虑x=2的情况: 第一步:r=1 -> s=0, r=1, count=1,不输出。 第二步:r=11 -> s=5, r=1 -> 输出5,count=2,started=1。 第三步:r=11(再次)-> 然后s=5,r=1 -> 输出5,然后继续?因为r=1,所以会循环下去?这不对,因为111...1(偶数个1)才能被2整除?不对,实际上,111除以2余11111除以2:1111/2=555余1,所以永远不会结束?但题目保证有解且x不以5结尾,实际上,当x=2时,解是1/2?不对,题目要求是光棍数,即全部由1组成的数。实际上,2整除2是1,但1不是光棍数?不对,题目要求的光棍数就是由1组成的数,最小的光棍数就是1,而1除以2商0余1,所以不行。实际上,题目中说了,输入31,输出358422939068115位),所以x=2时,应该是2整除2?不对,题目要求的是全部由1组成的数。实际上,2整除的是2,但2不是由1组成的。所以我们需要重新审视题目。 引用[1]中提示:输入31,输出358422939068115,因为31乘以3584229390681的结果是11111111111111115个1)。所以题目是找一个最小的全部由1组成的数(光棍数)能够整除x,然后输出这个光棍数除以x的商和光棍数的位数。 所以,当x=2时,最小的光棍数(由1组成的)能够整除2的是1111/2=5.5,不是整数。下一个是111111/2=55.5,也不是。实际上,由1组成的数,如果位数是偶数,则能被11整除,但不能被2整除(因为最后一位是1,是奇数)。所以,实际上不存在偶数个1能被2整除?不对,11除以2余1111除以2余1,所以实际上,不存在由1组成的数能够被2整除?但题目保证输入整数不以5结尾,所以2是以2结尾?题目说“输入不以5结尾”,所以x=2是以2结尾,所以题目保证有解?这里似乎矛盾。 实际上,题目描述为:“这里所谓的‘光棍’,并不是指单身汪啦~ 说的是全部由1组成的数字,比如1111111111等。传说任何一个光棍都能被一个不以5结尾的奇数整除。比如,111111就可以被13整除。 现在,你的程序要读入一个整数x,这个整数一定是奇数并且不以5结尾。然后,经过计算,输出两个数字:第一个数字s,表示x乘以s是一个光棍数,第二个数字n是这个光棍数的位数。这样的解当然不是唯一的,题目要求你输出最小的解。” 所以,题目明确输入x是一个奇数且不以5结尾。所以x=2的情况不会出现。因此我们在代码中不需要考虑x为偶数的情况。 因此,上述代码在x为奇数且不以5结尾的前提下是正确的。 但是,上面的代码在遇到需要输出中间有0的情况是否也能正确呢? 比如x=3,我们输出37(前面已经分析过),但实际应该是37(111除以3等于37)。 再测试x=7:最小的光棍数除以7是多少? 7*15873=111111(6个1),所以应该输出15873和6。 按照代码模拟: 开始:r=0 -> r=0*10+1=1, count=1 s=1/7=0, r=1 -> 不输出,started=0 第二步:r=1*10+1=11, count=2 s=11/7=1, r=4 -> 输出1(此时started=1),所以输出1 第三步:r=4*10+1=41, count=3 s=41/7=5, r=6 -> 输出5 第四步:r=6*10+1=61, count=4 s=61/7=8, r=5 -> 输出8 第五步:r=5*10+1=51, count=5 s=51/7=7, r=2 -> 输出7 第六步:r=2*10+1=21, count=6 s=21/7=3, r=0 -> 输出3 然后退出循环,输出“15873 6”,正确(注意,我们输出的顺序是每一步的商:1,5,8,7,3,所以是15873,但实际输出是每一步输出一个数字,所以连起来就是15873)。 但是,如果商中间有0呢?比如x=11,最小的光棍数应该是11(2个1),因为11除以11等于1。 第一步:r=0*10+1=1, count=1 s=1/11=0, r=1 -> 不输出(started=0) 第二步:r=1*10+1=11, count=2 s=11/11=1, r=0 -> 此时应该输出1(因为此时s=1,非0,所以输出1,started=1),然后退出。 所以输出“1 2”,正确。 再考虑x=111,最小光棍数就是111,位数3,商1。 第一步:r=1, count=1 -> s=1/111=0, r=1 -> 不输出 第二步:r=11, count=2 -> s=11/111=0, r=11 -> 不输出 第三步:r=111, count=3 -> s=1, r=0 -> 输出1,然后输出空格和3。所以输出“1 3”,正确。 再考虑一个商中间有0的情况:x=101,最小光棍数是1111(4个1),因为1111/101=11(商11)?不对,101*11=1111,正确,位数是4。 模拟: 第一步:r=1, count=1 -> s=1/101=0, r=1 -> 不输出 第二步:r=11, count=2 -> s=11/101=0, r=11 -> 不输出 第三步:r=111, count=3 -> s=111/101=1, r=10 -> 输出1(started=1) 第四步:r=10*10+1=101, count=4 -> s=101/101=1, r=0 -> 输出1 所以输出“11”(先是第三步的1,再是第四步的1)和4,正确。 但是,如果我们遇到商中间有0,比如x=201(奇数,不以5结尾),那么最小的光棍数是多少?201*551=111111(6个1)?201*551=110751,不对。实际上,201*552=110952,不对。我们可能需要计算:最小的光棍数位数应该是201的欧拉函数?但题目保证有解,所以我们模拟: 按照程序步骤: 第一步:r=1 -> s=0, r=1, count=1 -> 不输出 第二步:r=11 -> s=0, r=11, count=2 -> 不输出 第三步:r=111 -> s=0, r=111, count=3 -> 不输出 第四步:r=1111 -> s=5(因为1111/201=5,因为201*5=1005),r=1111-1005=106 -> 输出5(started=1) 第五步:r=1060+1=1061 -> s=1061/201=5(201*5=1005),r=1061-1005=56 -> 输出5 第六步:r=560+1=561 -> s=561/201=2(201*2=402),r=561-402=159 -> 输出2 第七步:r=1590+1=1591 -> s=1591/201=7(201*7=1407),r=184 -> 输出7 第八步:r=1840+1=1841 -> s=9(201*9=1809),r=32 -> 输出9 第九步:r=320+1=321 -> s=1(201*1=201),r=120 -> 输出1 第十步:r=1200+1=1201 -> s=5(201*5=1005),r=196 -> 输出5 第十一步:r=1960+1=1961 -> s=9(201*9=1809),r=152 -> 输出9 第十二步:r=1520+1=1521 -> s=7(201*7=1407),r=114 -> 输出7 第十三步:r=1140+1=1141 -> s=5(201*5=1005),r=136 -> 输出5 ... 这样一直循环,直到余数为0?但是201应该整除111111(实际上201*551=110751,不对)?实际上,201111111111111÷201=552.791...,不是整数。我们需要找到最小的光棍数。 实际上,根据题目,最小的光棍数位数应该是201的倍数,我们可以计算:20110互质(因为201的因数有3、67,不含2或5),所以最小光棍数的位数是201的一个倍数,最小可能是φ(201)=132的约数?但更直接的是,我们模拟除法直到余数为0,由于余数范围在0到200,所以最多201步内会循环,但题目保证有解,所以一定会结束。 实际上,201的最小光棍数是几位?通过计算,201整除11111111111112个1)?不对,我们继续模拟直到余数为0。但这样循环次数可能很大,不过题目保证有解并且x是奇数且不以5结尾,所以循环次数不会超过x(因为余数只有0到x-1,所以最多x步就会重复或者达到0)。但上面的代码会一直运行直到余数为0,所以没有问题。 但是,我们注意到,上面的代码在商0的时候(在开始输出之后)会输出0吗?例如,假设在中间某一步,商为0,但是已经开始了,那么就会输出0。这种情况是否存在? 考虑x=1001(奇数,不以5结尾)。1001*111=111111(6个1),所以最小的解是111111(6个1),商111。所以商不会出现0。但是,如果我们构造一个商中间有0的情况,比如商是101,那么我们在除法过程中会得到商1,0,1。这种情况是否存在? 假设x=111(但是111*1=111,商就是1,没有0)。或者考虑x=1001,商111也没有0。实际上,由于被除数是由1组成的数,而除数x是奇数且不以5结尾,每一步的余数我们乘以10加上1,然后再除以x,商可能是0到9之间的数。但是,在开始输出之后,如果某一步的商是0,那么就会输出0。那么,是否存在这样的情况? 例如,设x=1001,我们模拟: 第一步:1 -> 1/1001=0,余1 -> 不输出(count=1) 第二步:11 -> 11/1001=0,余11 -> 不输出(count=2) 第三步:111 -> 111/1001=0,余111 -> 不输出(count=3) 第四步:1111 -> 1111/1001=1(商1),余110 -> 输出1(开始输出),count=4 第五步:1100+1=1101 -> 1101/1001=1(商1),余100 -> 输出1,count=5 第六步:1000+1=1001 -> 1001/1001=1(商1),余0 -> 输出1,结束。所以输出“111 6”,正确。 但是,如果我们取一个更大的x,使得商中间出现0?比如x=3001(奇数,不以5结尾),那么最小的光棍数是多少? 3001*37037=111111111(9个1)?3001*37037=111,171,137,不对。实际上,我们可能需要模拟除法: 第一步:1 -> 0余1 第二步:11 -> 0余11 第三步:111 -> 0余111 第四步:1111 -> 0余1111 第五步:11111 -> 3(3001*3=9003),余2108 -> 输出3(started=1) 第六步:21081 -> 21081/3001=7(3001*7=21007),余74 -> 输出7 第七步:741 -> 741/3001=0,余741 -> 输出0(因为已经开始了) 第八步:7411 -> 7411/3001=2(3001*2=6002),余1409 -> 输出2 第九步:14091 -> 14091/3001=4(3001*4=12004),余2087 -> 输出4 第十步:20871 -> 20871/3001=6(3001*6=18006),余2865 -> 输出6 第十一步:28651 -> 28651/3001=9(3001*9=27009),余1642 -> 输出9 第十二步:16421 -> 16421/3001=5(3001*5=15005),余1416 -> 输出5 第十三步:14161 -> 14161/3001=4(3001*4=12004),余2157 -> 输出4 ... 这样下去,直到余数为0? 这个例子可能不会很快结束。但我们可以假设一个商中间有0的情况。例如,假设某一步的商为0,那么一定是当前被除数(余数*10+1)小于x。而由于之前已经输出过非0商,所以started=1,所以会输出0。 因此,上面的代码可以处理商中间出现0的情况。 所以,代码是可行的。 但是,上面的代码PTA上提交时,如果x很大,循环次数可能会很多(但最多循环x次,因为余数范围在0到x-1,最多x次就会重复,而题目保证有解,所以余数为0会在最多x次内出现)。但是x最大可能是接近1000(题目没有说明,但常见题目x在int范围内,且保证有解),所以循环次数最多1000多次,不会超时。 因此,我们提供上述代码作为解答。 注意:在输出最后,我们输出一个空格和位数,然后换行。 但是,有一个特殊情况:如果x=1?那么第一次循环:r=1,s=1/1=1,r=0,所以输出1,然后break,输出空格和1。所以输出“1 1”,正确。 因此,代码完整。 现在,根据用户要求,我们提供7-3整除光棍的解题思路和代码示例。 注意:用户之前引用的内容是关于光棍数的,所以我们的回答也是针对这个题目。 </think>### 翁恺C语言PTA习题7-3:整除光棍 #### **题目要求** 输入一个奇数且不以5结尾的正整数$x$,找到最小的由全1组成的数字(光棍数)使其能被$x$整除,输出对应的商和光棍数的位数。 **示例**:输入`31`,输出`3584229390681 15`(因为$31 \times 3584229390681 = 111111111111111$(15个1))[^1]。 --- #### **解题思路** 1. **问题难点**: 光棍数可能极大(如$10^{1000}$级别),无法用常规整数类型存储,需模拟竖式除法过程。 2. **关键算法**: - **模拟除法**:逐位生成商,避免直接存储大数 - 余数$r$初始为$0$,位数计数器$count$初始为$0$ - 循环执行: $$r = r \times 10 + 1$$ $$count \leftarrow count + 1$$ 计算当前商$s = \lfloor r / x \rfloor$,更新余数$r \leftarrow r \mod x$ - 当$r=0$时终止循环 - **处理前导零**: 用标志位$started$跳过商的前导零,遇到首个非零商后输出后续所有位(包括中间零)。 3. **数学原理**: 设光棍数为$\underbrace{11\cdots1}_{n\text{个}}$,可表示为$\frac{10^n-1}{9}$。题目等价于求最小$n$使得: $$x \ \bigg| \ \frac{10^n-1}{9}$$ 因$x$与$10$互质(不以5结尾),解必然存在[^1]。 --- #### **代码实现** ```c #include <stdio.h> int main() { int x; scanf("%d", &x); int r = 0; // 余数 int count = 0; // 光棍数位数计数器 int started = 0; // 标记是否已输出首个非零商 while (1) { r = r * 10 + 1; // 在余数后添1 count++; int s = r / x; // 当前位的商 r = r % x; // 更新余数 // 跳过前导零,遇到非零商后输出所有位 if (s != 0 || started) { printf("%d", s); started = 1; // 标记已开始输出 } // 余数为0时终止循环 if (r == 0) break; } printf(" %d\n", count); // 输出空格和位数 return 0; } ``` --- #### **代码解析** 1. **输入处理**: `scanf("%d", &x)`读取满足条件的整数$x$。 2. **模拟除法核心**: - **循环添1**:`r = r * 10 + 1`模拟光棍数的逐位构造 - **商与余数**:`s = r / x`计算当前商位,`r = r % x`更新余数 - **位数记录**:`count++`统计光棍数的总位数 3. **输出控制**: - `started`标志确保: - 首个非零商之前的前导零不输出(如$x=3$时跳过初始的$0$) - 首个非零商之后的所有位(包括零)均输出 4. **终止条件**: 当余数$r=0$时,光棍数已被整除,输出空格和总位数`count`。 --- #### **测试用例** | 输入 | 输出 | 验证($x \times$商 = 光棍数) | |-------|---------------|----------------------------------| | `31` | `3584229390681 15` | $31 \times 3584229390681 = 111111111111111$ | | `3` | `37 3` | $3 \times 37 = 111$ | | `7` | `15873 6` | $7 \times 15873 = 111111$ | | `101` | `11 4` | $101 \times 11 = 1111$ | > **关键点**: > - 避免直接处理大数,时空复杂度$O(n)$($n$为光棍数的位数) > - 余数$r$的范围$[0, x-1]$,保证循环最多$x$次[^1]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值