100 可以表示为带分数的形式:100 = 3 + 69258 / 714
还可以表示为:100 = 82 + 3546 / 197
注意特征:带分数中,数字1~9分别出现且只出现一次(不包含0)。
类似这样的带分数,100 有 11 种表示法。
题目要求:
从标准输入读入一个正整数N (N<1000*1000)
程序输出该数字用数码1~9不重复不遗漏地组成带分数表示的全部种数。
注意:不要求输出每个表示,只统计有多少表示法!
例如:
用户输入:
100
程序输出:
11
再例如:
用户输入:
105
程序输出:
6
资源约定:
峰值内存消耗(含虚拟机) < 64M
CPU消耗 < 3000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.6及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。
分析:这道题初看起来第一感觉就是用暴力破解应该可以搞定,但是时间复杂度应该会相当可观,仔细观察,会发现这道题无非是全排列的一种运用,把等式定义为:
N=A+B/C ,则ABC组合在一起就是1到9的一个全排列,所以可以把问题转换成对于一个9位数的数字,如何将其划分为A、B、C三部分,使得其满足N=A+B/C(隐含条件:
B%C==0),对于一个9位数可以这样考虑:A是不可能大于N的,所以A的位数只可能是从1位到和N相同位数这个范围,确定了A的位数之后,剩下的就是B和C的总位数,
B数字的开始位置即A数字的下一位,C数字的最后一位就是整个9位数的最后一位,那如何确定B的结束位置呢?
这里有个小技巧,可以大大减少可能性的判断:
假设A的结束位置为 aEnd,则aEnd+1~9就是B和C的位置,在这个位置范围内,B最少占据了一半的数字,否则B/C就不能整除了,所以可以从aEnd+1~9的中间位置开始
确定B的结束位置,这时候可以从中间位置开始向后确定B的结束位置,一直到8的位置,确定了B的结束位置,则A、B、C三个数字的具体值就都可以确定了,判断是否符
合等式N=A+B/C,符合则输出。
以上确定B的结束位置的方法其实不怎么好,因为还是会浪费一些时间(自己模拟下就知道了),不过已经可以在规定的时间内得出答案了。
这里再介绍一种确定B结束位置的方法,可以让性能再提高一些:
观察等式:N=A+B/C,可以转换成==》B=(N-A)*C,N和A确定了(先确定A的结束位置后,再来确定B的结束位置的),C的最后一个数字确定了(整个9位数最后一位),即可以确定B最后一个数字了(这里将其定义为BL),这样可以从以上的aEnd+1~9的中间位置开始找,直到8,当数字为BL时,则判读是否符合等式:N=A+B/C,可以想想,其实这种等于BL的位置至多有一次(因为数字1到9不能重复出现),所以第一次找到和BL匹配的数字的时候就不用再往后找了。用这种方法,提高的性能还是非常可观的!
思想其实挺简单,想不到打成字这么麻烦,将就着看吧,以下为java版的代码:
[java] view plaincopyprint?
- import java.util.Scanner;
- public class ys_09 {
- public static void main(String[] args) {
- //将等式定义为:N=A+B/C
- Scanner scanner=new Scanner(System.in);
- N=scanner.nextInt();
- long start=System.currentTimeMillis();
- int[] s=new int[]{1,2,3,4,5,6,7,8,9};
- NLength=(N+"").length();
- allRange(s, 0, s.length-1);
- long end=System.currentTimeMillis();
- System.out.println("耗时:"+(end-start)+" ms");
- System.out.println("总数为:"+kinds+" 种");
- }
- public static int N;
- public static int NLength;//N数字的长度
- public static int kinds;
- public static void process(int[] s){
- String str="";
- for(int i=0;i<9;i++) str+=s[i];
- int A,B,C,NMA,BC,BMCL,BLastNumber;
- //A的位数
- for(int i=1;i<=NLength;i++){
- /*
- //方法1
- A=Integer.valueOf(str.substring(0, i));
- NMA=N-A;//N减去A的值
- if(NMA<=0)return;
- BC=9-i;//B和C还有多少为可用
- BMCL=(NMA+"").length();//B/C的长度
- //确定的B的结束为止
- for(int j=i+BC/2;j<=8;j++){//可以优化这里
- B=Integer.valueOf(str.substring(i,j));
- C=Integer.valueOf(str.substring(j,9));
- if(B%C==0&&B/C==NMA){
- kinds++;
- System.out.println(N+"="+A+"+"+B+"/"+C);
- }
- }
- */
- //方法2
- A=Integer.valueOf(str.substring(0, i));
- NMA=N-A;
- if(NMA<=0)return;
- BC=9-i;//B和C总共多少位
- BLastNumber=(NMA*s[8])%10;//B最后的数字
- //j为B最后一个数字的位置
- //B最少占有B和C全部数字的一半,否则B/C不可能为整数
- for(int j=i+BC/2-1;j<=7;j++){
- //找到符合的位置
- if(s[j]==BLastNumber){
- B=Integer.valueOf(str.substring(i,j+1));
- C=Integer.valueOf(str.substring(j+1,9));
- if(B%C==0&&B/C==NMA){
- kinds++;
- System.out.println(N+"="+A+"+"+B+"/"+C);
- }
- //符合要求的位置只可能出现一次
- break;
- }
- }
- }
- }
- public static void swap(int[] s,int a,int b){
- if(a==b)return;
- int tmp=s[a];
- s[a]=s[b];
- s[b]=tmp;
- }
- //全排列
- public static void allRange(int[] s,int k,int m){
- if(k==m){
- process(s);
- return;
- }
- else{
- for(int i=k;i<=m;i++){
- swap(s,k,i);
- allRange(s, k+1, m);
- swap(s,k,i);
- }
- }
- }
- }
import java.util.Scanner; public class ys_09 { public static void main(String[] args) { //将等式定义为:N=A+B/C Scanner scanner=new Scanner(System.in); N=scanner.nextInt(); long start=System.currentTimeMillis(); int[] s=new int[]{1,2,3,4,5,6,7,8,9}; NLength=(N+"").length(); allRange(s, 0, s.length-1); long end=System.currentTimeMillis(); System.out.println("耗时:"+(end-start)+" ms"); System.out.println("总数为:"+kinds+" 种"); } public static int N; public static int NLength;//N数字的长度 public static int kinds; public static void process(int[] s){ String str=""; for(int i=0;i<9;i++) str+=s[i]; int A,B,C,NMA,BC,BMCL,BLastNumber; //A的位数 for(int i=1;i<=NLength;i++){ /* //方法1 A=Integer.valueOf(str.substring(0, i)); NMA=N-A;//N减去A的值 if(NMA<=0)return; BC=9-i;//B和C还有多少为可用 BMCL=(NMA+"").length();//B/C的长度 //确定的B的结束为止 for(int j=i+BC/2;j<=8;j++){//可以优化这里 B=Integer.valueOf(str.substring(i,j)); C=Integer.valueOf(str.substring(j,9)); if(B%C==0&&B/C==NMA){ kinds++; System.out.println(N+"="+A+"+"+B+"/"+C); } } */ //方法2 A=Integer.valueOf(str.substring(0, i)); NMA=N-A; if(NMA<=0)return; BC=9-i;//B和C总共多少位 BLastNumber=(NMA*s[8])%10;//B最后的数字 //j为B最后一个数字的位置 //B最少占有B和C全部数字的一半,否则B/C不可能为整数 for(int j=i+BC/2-1;j<=7;j++){ //找到符合的位置 if(s[j]==BLastNumber){ B=Integer.valueOf(str.substring(i,j+1)); C=Integer.valueOf(str.substring(j+1,9)); if(B%C==0&&B/C==NMA){ kinds++; System.out.println(N+"="+A+"+"+B+"/"+C); } //符合要求的位置只可能出现一次 break; } } } } public static void swap(int[] s,int a,int b){ if(a==b)return; int tmp=s[a]; s[a]=s[b]; s[b]=tmp; } //全排列 public static void allRange(int[] s,int k,int m){ if(k==m){ process(s); return; } else{ for(int i=k;i<=m;i++){ swap(s,k,i); allRange(s, k+1, m); swap(s,k,i); } } } }
输入:100
输出:
[java] view plaincopyprint?
- 100=3+69258/714
- 100=82+3546/197
- 100=81+5643/297
- 100=81+7524/396
- 100=94+1578/263
- 100=96+2148/537
- 100=96+1428/357
- 100=96+1752/438
- 100=91+5742/638
- 100=91+5823/647
- 100=91+7524/836
- 耗时:554 ms
- 总数为:11 种