P8615 [蓝桥杯 2014 国 C] 拼接平方数
题目描述
小明发现 49 49 49 很有趣,首先,它是个平方数。它可以拆分为 4 4 4 和 9 9 9,拆分出来的部分也是平方数。 169 169 169 也有这个性质,我们权且称它们为:拼接平方数。
100 100 100 可拆分 1 , 00 1,00 1,00,这有点勉强,我们规定, 0 , 00 , 000 0,00,000 0,00,000 等都不算平方数。
小明想:还有哪些数字是这样的呢?
你的任务出现了:找到某个区间的所有拼接平方数。
输入格式
两个正整数 a , b ( a < b < 1 0 6 ) a,b(a<b<10^6) a,b(a<b<106)。
输出格式
若干行,每行一个正整数。表示所有的区间 [ a , b ] [a,b] [a,b] 中的拼接平方数,从小到大输出。
输入输出样例 #1
输入 #1
169 10000
输出 #1
169
361
1225
1444
1681
3249
4225
4900
9025
说明/提示
时限 1 秒, 256M。蓝桥杯 2014 年第五届国赛
洛谷链接
思路讲解
先保证自己是平方数,再尝试所有"断位"拆法,看能不能拆出两个非零平方数。
java
import java.util.Scanner;
public class Main {
// 判断是不是平方数
public static boolean isPerfectSquare(int n) {
if (n < 0) {
return false;
}
int num = (int) Math.sqrt(n);
return num * num == n;
}
// 拆分后各自判断是不是拼接平方数
public static boolean couldFormSquare(int num) {
if (isPerfectSquare(num)) {
// 按 10/100/1000... 的位置拆分
for (int i = 10; i < num; i *= 10) {
int numRight = num % i; // 右边部分
int numLeft = num / i; // 左边部分
// 两边都非 0 且都是平方数即可返回 true
if (numRight != 0 && numLeft != 0 && isPerfectSquare(numRight) && isPerfectSquare(numLeft)) {
return true;
}
}
return false;
}
return false;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int a = sc.nextInt();
int b = sc.nextInt();
// 遍历区间 [a, b] 并输出所有拼接平方数
for (int n = a; n <= b; n++) {
if (couldFormSquare(n)) {
System.out.println(n);
}
}
}
}
但是,这段代码还有些不足。isPerfectSquare 每轮都调用 Math.sqrt,在 1e6 范围里 不会超时,但可以提前打表,把 0‥1 000 000 里所有平方数 一次性标记出来,后面 O(1) 查询 即可,这样大幅提升了效率。
什么是打表?
打表其实就是 "先把答案算出来,存到数组里,用的时候直接查"
空间换时间
- 开一块布尔数组
java
static boolean[] isSquare = new boolean[1_000_001]; // 索引 0..1 000 000
- 代码运行时,一次性把平方数全标成 true,存放于数组中
java
static { // 静态代码块只执行一次
for (int i = 0; (long) i * i <= 1_000_000; i++) { // i*i 可能超 int,强转 long
isSquare[i * i] = true;
// 把 0,1,4,9,16...1 000 000 全标 true
}
}
- 以后任何地方想判断 n 是不是平方数,直接isSquare[n]查询,时间复杂度为O(1)
java
if (isSquare[n]) { ... } // O(1) 查询,不用再算 sqrt
完整代码如下:
java
import java.util.Scanner;
public class Main1 {
//可以提前打表,把 0‥1 000 000 里所有平方数 一次性标记出来,后面 O(1) 查询 即可。
static boolean[] isSquare = new boolean[1_000_001];
static {
for (int i = 0; (long) i * i <= 1_000_000; i++) {
isSquare[i * i] = true;
}
}
//拆分后各自判断是不是拼接平方数
public static boolean couldFormSquare(int num) {
if (isSquare[num]) {
for (int i = 10; i < num; i *= 10) {
int numRight = num % i;
int numLeft = num / i;
if (isSquare[numRight] && isSquare[numLeft] && numRight != 0 && numLeft != 0) {
return true;
}
}
return false;
}
return false;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int a = sc.nextInt();
int b = sc.nextInt();
for (int n = a; n <= b; n++) {
if (couldFormSquare(n)) {
System.out.println(n);
}
}
}
}