问题描述
100 可以表示为带分数的形式:100 = 3 + 69258 / 714。
还可以表示为:100 = 82 + 3546 / 197。
注意特征:带分数中,数字1~9分别出现且只出现一次(不包含0)。
类似这样的带分数,100 有 11 种表示法。
输入格式
从标准输入读入一个正整数N (N<1000*1000)
输出格式
程序输出该数字用数码1~9不重复不遗漏地组成带分数表示的全部种数。
注意:不要求输出每个表示,只统计有多少表示法!
样例输入1
100
样例输出1
11
样例输入2
105
样例输出2
6
思路一:
全排列列出所有的情况,求出满足条件的个数,不过会超时,这时要剪枝
如何建造好的剪枝条件?
本题说的是,n=a+b/c;那么首先a一定是小于n的,又因为n为整数,所以a和b/c都是整数,这就要求
b/c一定可以整除,所以b%c=0,b/c还要满足可除条件,即b>=c。剪枝的三个条件已经确定
(1).a<n;
(2).b%c=0;
(3).b>=c
再加上n=a+b/c就是四个条件了。只要在1至9的全排列中选取满足这四个条件的全排列就是所求的结果之一。
那么在1至9的全排列(9个数字)中如何确定a,b,c的取值范围呢?
a前面已经说过,而又知道,b一定大于或等于c,则b的取值范围一定在a选择过后去选择剩下的一半或一半以上的数据。举个例子,1至9的其中一个全排列--156987423,若a选择156,则b只能选择剩下的987423中的一半或
一半以上,如987、9874、98742。如果b小于剩下的一半,那么一定不满足除法(如98/7432)。c
的范围则是a和b选择剩下的所有了。这样我们就可以判定,假设num=9,a选择9位中的前n位,那
么b的结尾选择范围为第n+(num-n)/2至num-1位数字(结尾为一半或一半以上,最多时到num-1
,给c留一个数字);
那么利用深度优先搜索(用来得到一个9位的全排列)和适当的判断(剪枝,找出符合3个条件并
且满足n=a+b/c的全排列)就可以解决。
下面是我写的两个版本的代码
版本一 (超时)
1 import java.util.Scanner;
2 import java.util.concurrent.atomic.LongAccumulator;
3
4 public class Main{
5 public static int num=0;
6 public static int[]array=new int[10];
7 static int res;
8 static int length=0;
9 public static void main(String[] args){
10 Scanner scanner=new Scanner(System.in);
11 String th=scanner.next();
12 res=Integer.parseInt(th);
13 length=th.length();
14 for (int i = 1; i <=array.length-1; i++) {
15 array[i]=i;
16 }
17 long a=System.currentTimeMillis();
18 perm(array,1, 9);
19 System.out.println(num);
20 long b=System.currentTimeMillis();
21 // System.out.println(b-a);
22
23 }
24 public static void Swap(int str[], int a, int b)
25 {
26 int temp = str[a];
27 str[a] = str[b];
28 str[b] = temp;
29 }
30 public static void perm(int array[],int k,int m)
31 {
32 if(k==m) {
33 String string="";
34 for (int i = 1; i <=array.length-1; i++) {
35 string+=array[i];
36 }
37 String a="";
38 String b="";
39 String c="";
40 //int num1=Integer.parseInt(string);
41 for (int i = 1; i <string.length()-1; i++) {
42 a=string.substring(0, i);
43 int a1=Integer.parseInt(a);
44 if(a1>res) break;
45 for (int j = i+(9-i)/2; j < string.length(); j++) {
46
47 b=string.substring(i,j);
48 c=string.substring(j,string.length());
49
50
51
52 int a2=Integer.parseInt(b);
53 int a3=Integer.parseInt(c);
54 if(a3>a2) continue;
55 if(a2%a3==0) {
56 if(a1+a2/a3==res) {
57 num++;
58 }
59 }
60 }
61 }
62 return;
63
64 }else {
65 for (int i = k; i <=m; i++) {
66 Swap(array, i, k);
67 perm(array,k+1,m);
68 Swap(array, i, k);
69 }
70 }
71
72
73 }
74
75 }
版本二(正确)
1 import java.util.Scanner;
2 import java.util.concurrent.atomic.LongAccumulator;
3 public class Main{
4 public static int num=0;
5 public static int[]array=new int[10];
6 static int res;
7 static int length=0;
8 public static void main(String[] args){
9 Scanner scanner=new Scanner(System.in);
10 String th=scanner.next();
11 long a=System.currentTimeMillis();
12 res=Integer.parseInt(th);
13 length=th.length();
14 for (int i = 1; i <=array.length-1; i++) {
15 array[i]=i;
16 }
17
18 perm(array,1, 9);
19 System.out.println(num);
20 long b=System.currentTimeMillis();
21 // System.out.println(b-a);//显示运行时间,单位ms
22
23 }
24 public static void Swap(int str[], int a, int b)
25 {
26 int temp = str[a];
27 str[a] = str[b];
28 str[b] = temp;
29 }
30 public static int sumNUm(int array[],int start,int end)
31 {
32 int sum=0;
33 for (int i = start; i <=end; i++) {
34 sum=sum*10+array[i];
35
36 }
37 return sum;
38 }
39 public static void perm(int array[],int k,int m)
40 {
41 if(k==m) {
42 for (int i = 1; i <array.length-2; i++) {
43 int a=sumNUm(array, 1, i);
44 if(a>res) break;
45 for (int j = i+(9-i)/2; j <array.length-1; j++) {
46 int b=sumNUm(array, i+1, j);
47 int c=sumNUm(array, j+1, m);
48 if(c>b) continue;
49 if(b%c==0&& b/c+a==res) {
50 //System.out.println(a+"+"+b+"/" + c);
51 num++;
52 }
53 }
54 }
55
56 }else {
57 for (int i = k; i <=m; i++) {
58 Swap(array, i, k);
59 perm(array,k+1,m);
60 Swap(array, i, k);
61 }
62 }
63
64
65 }
66
67 }
两个版本思路完全一样,不同的是版本一的代码把数组变成了字符串,再进行拆分不同数字,结果超时,充分说明了计算机对字符串的处理速度是比较慢的,所以以后在使用字符串时要注意效率和速度。
思路二
没有用到全排列的一种暴力手段,也不会超时。
作者:你的雷哥
本文版权归作者所有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。