又是翻题解才能想出来的思路。。
所有数字都在“123456789”里取,且每个数字只能出现一次
枚举两个分割点,把整个排列分为三个区间,每个区间表示一个数,如“12|345|6789”对应$12+\frac{345}{6789}$
可以按长度枚举,前两个数所占的长度都从小到大,剩下的归到第三个数,则分别记为$x$、$y$、$z$(其中$x+\frac yz =n$),期间$x$、$y$越来越大而$z$越来越小,当$x \geq n$或者$\frac yz \geq n-x$时,当前排列后续的所有分割情况都不满足了
接着推导出“123456789”的下一个排列,并再次枚举分割点,直到尝试完所有的排列情况
求下一个排列,可以手撸nextPermutation
,参照本人题解AcWing 420. 火星人(Java 图示 实现next_permutation)
import java.util.*;
import java.lang.*;
public class Main {
static Scanner scanner = new Scanner(System.in);
static boolean nextPermutation(char[] a, int l, int r) {
int pos1 = r;
while (pos1 > l && a[pos1 - 1] >= a[pos1]) pos1--;
pos1--;
if (pos1 < l) return false; // 如果当前已经是最后、最大的一个排列 就返回false
int pos2 = pos1 + 1;
while (pos2 <= r && a[pos2] >= a[pos1]) pos2++;
pos2--;
char t = a[pos2];
a[pos2] = a[pos1];
a[pos1] = t;
for (int i = pos1 + 1, j = r; i < j; i++, j--) {
t = a[i];
a[i] = a[j];
a[j] = t;
}
return true; // 成功生成下一个排列 返回ture
}
public static void main(String[] args) {
int n = scanner.nextInt();
char[] a = "123456789".toCharArray(); // 初始指定排列为最小字典序
int cnt = 0; // 记录方案总数
do {
for (int i = 1; i <= a.length - 2; i++) { // 枚举第一个数占序列的字符个数
int x = -1, y = -1, z = -1; // x + y/z == n
for (int j = 1; j + i <= a.length - 1; j++) { // 枚举第二个数占序列的字符个数
x = Integer.parseInt(String.valueOf(a, 0, i)); // 依次从字符数组转到数字
y = Integer.parseInt(String.valueOf(a, i, j));
z = Integer.parseInt(String.valueOf(a, i + j, a.length - i - j));
if (y % z == 0 && x + y / z == n) cnt++; // 如果可行 方案加1
else if (y / z >= n - x) break; // 剪枝1 长度从小到大枚举 x不变时y越来越大z越来越小 y/z超过n-x就可以停了
}
if (x >= n) break; // 剪枝2 当x已经大于等于n时 +y/z必定不满足 可以直接开始下一个排列
}
} while (nextPermutation(a, 0, a.length - 1)); // 枚举所有排列情况
System.out.println(cnt);
}
}