100 100 100 可以表示为带分数的形式: 100 = 3 + 69258 714 100=3+\frac{69258}{714} 100=3+71469258
还可以表示为: 100 = 82 + 3546 197 100=82+\frac{3546}{197} 100=82+1973546
注意特征:带分数中,数字 1 ∼ 9 1∼9 1∼9 分别出现且只出现一次(不包含 0 0 0)。
类似这样的带分数, 100 100 100 有 11 11 11 种表示法。
输入格式
一个正整数。
输出格式
输出输入数字用数码 1 ∼ 9 1∼9 1∼9 不重复不遗漏地组成带分数表示的全部种数。
数据范围
1 ≤ N < 1 0 6 1≤N<10^6 1≤N<106
输入样例1
100
输出样例1
11
输入样例2
105
输出样例2
6
根据题目描述,带分数满足 n = a + b c n = a + \frac{b}{c} n=a+cb。要计算 a , b , c a,b,c a,b,c的情况,可以将等式左右两边同时 × c \times c ×c得到 n c = a c + b nc = ac + b nc=ac+b。因此可以先枚举 a a a和 c c c,再计算 b b b的值,最后判断 a , b , c a,b,c a,b,c是否符合条件即可。
由于在带分数中数字 1 ∼ 9 1∼9 1∼9 分别出现且只出现一次,可以使用暴力搜索的方式先确定 a a a,对于 a a a的每种符合条件的取值,继续搜索 c c c的情况。对于一组 a a a和 c c c的取值,计算出 b b b,再判断 b b b是否符合条件即可。
#include
#include
using namespace std;
const int N = 20;
int n, ans;
int st[N], backup[N];
bool check(int a, int c)
{
//将带分数等式两边同时乘c,计算b
long long b = (long long) n * c - a * c;
//不能为0
if(!a || !b || !c) return 0;
//将st备份到backup,对b进行检查
memcpy(backup, st, sizeof st);
while(b)
{
int o = b % 10;
b /= 10;
//如果b的个位为0,或者已经使用过
if(!o || backup[o]) return 0;
backup[o] = 1;
}
//判断是否存在没有用到的数字
for(int i = 1; i <= 9; i ++)
if(!backup[i]) return 0;
return 1;
}
//对a的每个取值,搜索c的值
void dfs_c(int t, int a, int c)
{
//如果9个数字都已搜索过,结束搜索
if(t > 9) return;
//对一组a、c判断是否满足要求
if(check(a, c)) ans ++;
for(int i = 1; i <= 9; i ++)
{
if(!st[i])
{
st[i] = 1;
//将当前数字作为c的个位,继续向下搜索
dfs_c(t + 1, a, c * 10 + i);
//恢复现场
st[i] = 0;
}
}
}
//搜索a的取值,t表示已经确定的数字个数
void dfs_a(int t, int a)
{
//a作为带分数的一项,结果不能超过n
if(a >= n) return;
//对于a的每个合法取值(不能为0),继续搜索c的情况
if(a) dfs_c(t, a, 0);
for(int i = 1; i <= 9; i ++)
{
//每个数字只能出现一次
if(!st[i])
{
st[i] = 1;
//将当前数字作为a的个位,继续向下搜索
dfs_a(t + 1, a * 10 + i);
//恢复现场
st[i] = 0;
}
}
}
int main()
{
cin >> n;
dfs_a(0, 0);
cout << ans << endl;
return 0;
}
再提供一种时间复杂度更优的枚举思想。根据题目描述,在带分数中,数字 1 ∼ 9 1∼9 1∼9 分别出现且只出现一次(不包含 0 0 0)。可以推断在带分数中,分母不会超过 4 4 4位数,且最大值为9876
。
若分母超过4位数,为了使分数的结果为整数,则分子的位数至少为5位,不满足题意。
因此可以枚举分母,以及每个分母的整数倍,在其中选择符合条件的解即可。其中分母的整数倍不会超过 6 6 6位数字,且最大值为987654
在枚举的过程中,注意要对不符合条件的解及时剪枝,以降低时间复杂度。
#include
using namespace std;
//检查是否包含0,以及每个数的出现次数是否合法
bool check(int x)
{
int cnt[10] = {0};
while(x)
{
int o = x % 10;
if(cnt[o] || o == 0) return false;
cnt[o] ++;
x /= 10;
}
return true;
}
//检查1~9是不是只出现1次
bool check(int a, int b, int c)
{
int res = 0;
int cnt[10] = {0};
while(a)
{
int o = a % 10;
if(cnt[o] || o == 0) return false;
cnt[o] ++;
res ++;
a /= 10;
}
while(b)
{
int o = b % 10;
if(cnt[o] || o == 0) return false;
cnt[o] ++;
res ++;
b /= 10;
}
while(c)
{
int o = c % 10;
if(cnt[o] || o == 0) return false;
cnt[o] ++;
res ++;
c /= 10;
}
return res == 9;
}
int main()
{
int x;
cin >> x;
int res = 0;
//枚举分母
for(int i = 1; i <= 9876; i ++)
{
if(!check(i)) continue;
//枚举分母的倍数,注意倍数要小于x
for(int j = 2; j < x && i * j <= 987654; j ++)
{
if(!check(i * j)) continue;
int a = x - j;
if(check(a, i, i * j))
{
res ++;
//cout << a << " + " << i * j << " / " << i << endl;
}
}
}
cout << res << endl;
return 0;
}