带分数

带分数 


问题描述

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


Algorithm

一开始,常规思路进行枚举,因为等式形如:

 

那么考虑整除的问题,C最多不超过4位数,A不超过7位数,由等式我们可以算出B。则过程很清晰了:

分别枚举A和C,算出B,判断一下是否满足条件,计数即可。

但是仔细分析了一下时间复杂度,果然没有通过。先放一下代码:

 

 1 /*
 2 * N = A + B/C
 3 * A最多7位,C最多4位,算出B 
 4 */
 5 #include<iostream>
 6 #include<algorithm>
 7  
 8 using namespace std;
 9 
10 int c = 0,N = 0;;
11 
12 // 获取数字长度 
13 int size(int n)
14 {
15     int c = 0;
16     while(n)
17     {
18         n /= 10;
19         c++;
20     }
21     return c;
22 }
23 
24 bool sure(int A, int B, int C)
25 {
26     // 排除有0项 
27     if((size(A) + size(B) + size(C)) != 9)
28         return false;
29     if(A == C)
30         return false;
31     if(B <= 0)
32         return false;
33     int s[10] = {0};
34     // int a=A,b=B,c=C;
35     while(A!=0 || B!=0 || C!=0)
36     {
37         s[A%10]++;s[B%10]++;s[C%10]++;
38         A /= 10;B /= 10;C /= 10;
39     }
40     for(int i=1;i<=9;i++){
41         if(s[i] != 1)
42             return false;
43     }
44     // cout<<a<<":"<<b<<":"<<c<<endl;
45     return true;
46 }
47 
48 int solve(int n)
49 {
50     // 设置循环节加速,但效果甚小
51     int len[7] = {0, 9876, 987, 987, 98, 98, 9};
52     int A = 1, C = 2, B = 0;
53     for(A=1;A<=9876543;A++){ // 需要排除有0的项 
54         // 在这里的几个ABC也可以排除0项,但效果也甚微
55         for(C=2;C<=len[size(A)];C++){
56             B = C*(N - A);
57             if(sure(A, B, C))
58                 c++;
59         }
60     }
61     return c;
62 }
63 /*
64 * 加速了还是发现时间复杂度太高,约O(9876543 * 5000)
65 * 改用全排列试试 约O(7 * 9!)
66 * 速度提升大约 70*30 倍
67 */
68 int main()
69 {
70     while(cin>>N)
71     {
72         cout<<solve(N)<<endl;
73         c = 0;
74     }
75     return 0;
76 }

View Code

但是思路是没有问题的,接下来想到了以前做过的一道题目:桥本分数式

基本与本题一致,所以改用全排列的做法。枚举9个数的全排列确实要比循环每一个A、C快得多。

全排列可以用C++自带的 #include<algorithm> 头文件的 next_permutation 函数。也可以深度优先遍历。

用一个数组保存1 —  9之后,我们只需要尝试每一个组合的有限个分组即可,满足等式计数+1.

尽管还是有点吃力,但是勉强过了。


AC

 1 /*
 2 * N = A + B/C
 3 * A 最长不超过7位数, 这样我们确定一个 A 
 4 * 然后我们可以知道 C 的最后一位为 a[8]
 5 * 通过计算我们可以确定 B 的 最后一位 b_end = (N - A)*a[8]%10
 6 * 如此,我们就知道了 C 为多少
 7 * 最后,判断一下是否满足等式 
 8 */
 9 #include<iostream>
10 #include<algorithm>
11  
12 using namespace std;
13 
14 int c = 0,N = 0;;
15 int a[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
16 
17 // 这里复习一下整数快速幂,就不调用数学库了
18 // O(log2N)
19 int pow(int x, int n)
20 {
21     int s = 1;
22     while(n)
23     {
24         if(n&1) s *= x;
25         x *= x;
26         n >>= 1;
27     }
28     return s;
29 }
30 
31 // A、B、C, b开始, e-1结束的一段数组 
32 int PI(int b, int e)
33 {
34     int s = 0, k = e-b-1;
35     while(b!=e)
36     {
37         s += a[b]*pow(10, k);k--;b++;
38     }
39     return s;
40 }
41 
42 // 获取 B 最后一位的 local 
43 int getB_end(int x)
44 {
45     int loc = 0;
46     for(int i=0;i<9;i++)
47         if(a[i] == x)
48             return i;
49 }
50 
51 void solve(int *a, int N)
52 {
53     int A, B, C;A = B = C = 0;
54     // cout<<N<<endl;
55     for(int i=1;i<=7;i++){
56         A = PI(0, i);
57         int B_end = ((N - A)*a[8])%10;
58         int loc = getB_end(B_end);
59         if(loc < i || loc >= 8)
60             continue;
61         // Ai! 这里注意一下是 loc + 1 
62         B = PI(i, loc+1);
63         C = PI(loc+1, 9);    
64         // cout<<A<<":"<<B<<":"<<C<<endl;
65         // cout<<A<<":"<<B<<":"<<C<<endl;    
66         if(A*C + B == N*C){
67             // cout<<A<<":"<<B<<":"<<C<<endl;
68             c++;
69         }    
70     }
71 }
72 
73 int main()
74 {
75     while(cin>>N)
76     {
77         while(next_permutation(a, a+9))
78         {   // 时间复杂度可以看成 O(9!) 
79             solve(a, N);
80             // break;
81         }
82         cout<<c<<endl;c = 0;
83     }
84     return 0;
85 }

View Code


2019-01-15

19:09:38

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注