蓝桥杯-完美平方数-java解法

  • 完美平方数
  • 题目
  • 解法
  • 1. 正向解法
  • 2. 逆向解法


完美平方数

如果一个整数本身是完全平方数,它的每一位组成数字也是完全平方数,我们就称它是完美平方数。

前几个完美平方数是 0、1、4、9、49、100、144……

即第 1 个完美平方数是 0,第 2 个是 1,第 3 个是 4,……

题目

那么,第 2020 个完美平方数是多少?

解法

1. 正向解法

我们观察到,完美平方数是0及正整数,且每一位为0,1,4,9中的一个,所以可以组合拼接这四个数,然后验证拼接的数字是否能被开平方为整数。

下面上代码:

@Test
    public void perfectSquareNumber() {
        // 完美平方数  [解法一:使用0,1,4,9拼接组合字符,计算时间超长]
        int count = 1; // 完美平方数计数
        String[] eleArray = {"0", "1", "4", "9"};  // 元素组
        List<Integer> current = new ArrayList<>();
        current.add(0);
        int rIndex = 0; // 初始化改变位置的下标
        System.out.println("count=" + count + ", number=" + getCurrentStr(current, eleArray));
        while (true) {
            rIndex = calcRIndex(current, rIndex);
            // 验证是否能被开平方
            String currentStr = getCurrentStr(current, eleArray);
            double sqrt = Math.sqrt(Long.parseLong(currentStr));
            if (sqrt == (long) sqrt) {
                count++;
                System.out.println("count=" + count + ", number=" + currentStr);
                if (count >= 2020) {
                    break;
                }
            }
        }
    }

	// 计算改变位置的下标
	private int calcRIndex(List<Integer> current, int rIndex) {
        if (current.get(rIndex) < 3) {
            current.set(rIndex, current.get(rIndex) + 1);
            if (rIndex != current.size() - 1) {
                rIndex = current.size() - 1;
            }
        } else {
            // 升位或扩容
            if (rIndex == 0) {
                // 扩容
                current.set(0, 1);
                current.add(0);
                rIndex = current.size() - 1;
            } else {
                // 升位
                for (int i = rIndex; i < current.size(); i++) {
                    current.set(i, 0); // 复零
                }
                rIndex--;
                return calcRIndex(current, rIndex);
            }
        }
        return rIndex;
    }

		// 获取拼接的字符串
    private String getCurrentStr(List<Integer> current, String[] eleArray) {
		// System.out.print("current={");
        StringBuilder curStr = new StringBuilder("");
        for (int i = 0; i < current.size(); i++) {
		// System.out.print(current.get(i) + ", ");
            curStr.append(eleArray[current.get(i)]);
        }
		// System.out.println("}");
		// System.out.println(curStr.toString());
        return curStr.toString();
    }

上述代码存在问题,经过较长时间(约2分钟)只计算到300+,且长度已经超过12位,从蓝桥杯测试题的选择结果来看,这个数字并未超出 Integer.MAX_VALUE=2147483647。也许,我们可以采用别的方式来求出结果。

2. 逆向解法

实际上,完美平方数的根是0及正整数,那么,我们何不从0开始递增,平方后检查该结果是否符合完美平方数呢?

说干就干:

@Test
    public void perfectSquareNumber1() {
        // 完美平方数  [解法二:逆向思维:完美平方数的根都是整数...]
        StringBuilder squared;
        int count = 1;
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            squared = new StringBuilder(i * i + "");
            boolean inExp = true;
            for (int j = 0; j < squared.length(); j++) {
                switch (squared.charAt(j)) {
                    case '0':
                    case '1':
                    case '4':
                    case '9':
                        break;
                    default:
                        inExp = false;
                        break;
                }
                if (!inExp) {
                    break;
                }
            }
            if (inExp) {
                System.out.println("count=" + count + ", number=" + squared);
                if (count < 2021) {
                    count++;
                } else {
                    break;
                }
            }
        }
    }

思路很简单,写起来也很快,查看控制台运行时间500ms左右,完美解决。

count=2020, number=1499441040
count=2021, number=1949990009


2022-1-11
后期有读者朋友反馈,打印的数字“时大时小”,很可疑,于是在控制台打印了用于找出完美平方数的i,发现 count=2021 时 i=13793803,显然i^2已经远远超出Integer.MAX_VALUE,所以虽然最后的结果与蓝桥杯的答案对应,但那其实是数据溢出的结果,并不是真实答案。