问题描述
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的全排列)就可以解决。
本题说的是,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 }
两个版本思路完全一样,不同的是版本一的代码把数组变成了字符串,再进行拆分不同数字,结果超时,充分说明了计算机对字符串的处理速度是比较慢的,所以以后在使用字符串时要注意效率和速度。
思路二
可以参考这篇博客https://blog.csdn.net/zhangpengyu321/article/details/8964803
没有用到全排列的一种暴力手段,也不会超时。