带分数问题
来自网友这里提问的问题
http://ask.csdn.net/questions/242539
问题描述
100 可以表示为带分数的形式:100 = 3 + 69258 / 714
还可以表示为:100 = 82 + 3546 / 197
注意特征:带分数中,数字1~9分别出现且只出现一次(不包含0)。
题目要求:
从标准输入读入一个正整数N (N<1000*1000)
程序输出该数字用数码1~9不重复不遗漏地组成带分数表示的全部种数。例如:
用户输入:
100
程序输出:
11
再例如:
用户输入:
105
程序输出:
6
又是蓝桥杯的问题吧
思路
N = a + b/c
ar = N-a
an: a的数字个数
arn: ar的数字个数
bn: b的数字个数
cn: c的数字个数我们可以得到以下的数学表达式
1) an + bn +cn = 9
2) bn >= cn
3) arn*cn >= bn-1
4) an, bn, cn, arn >0step1:得到一个合适的a,没有重复数字和0,得到an,得到剩余数字的列表L
step2:根据N,a,an,可以得到ar,arn
step3:以cn为循环因子,带入an,arn,带入上面的4个表达式,可以得出一组或多组cn和bn的值
step4:根据剩余数字列表L和cn,可以得到c的取值范围cmin和cmax
step5:以c为循环因子,在[cmin,cmax]循环,根据c的值更新L。根ar = N-a=b/c,得到b=ar*c,更新L。如果L合理,hit。否则丢弃
参考代码
此处没有使用数组来表达L,用的是位掩码方式,更简洁。掩码用低10个bit表达数字9~0的占位符。bit31表示存在重复数字。
#include <stdio.h>
#define MAX_INPUT 1000000
#define DIGITAL_MASK ((1<<10)-1)
#define DIGITAL_NUM 9
int find_min(int mask, int digi_num)
{
int min = 0;
int digital = 1;
int tmp=0;
do{
if (mask&(1<<digital))
{
min = min*10 +digital;
tmp++;
}
mask &= ~(1<<digital);
digital++;
if (tmp == digi_num) break;
}while(digital<=DIGITAL_NUM);
return min;
}
int find_max(int mask, int digi_num)
{
int max = 0;
int digital = 9;
int tmp = 0;
do{
if (mask&(1<<digital))
{
max = max*10 +digital;
tmp++;
}
mask &= ~(1<<digital);
digital--;
if (tmp == digi_num) break;
}while(digital>0);
return max;
}
int update_mask(int data, int mask, int* digi_num)
{
int digital = 0;
if(digi_num!= NULL) (*digi_num) = 0;
do{
digital = data % 10;
if (!(mask & (1<<digital)))
{
mask |= (1<<31);
break;
}
mask &= ~(1<<digital);
data /= 10;
if(digi_num!= NULL) (*digi_num)++;
}while(data);
return mask;
}
int main()
{
int N = 0; // input
int a =0;
int ar = 0;
int b = 0;
int c = 0; // N = a + b/c
int cmin = 0;
int cmax = 0;
int an = 0;
int arn = 0;
int bn = 0;
int cn = 0;
int dissamble_num = 0;
int num_flag = DIGITAL_MASK; // bit mask of ten digitals 9~0
int num_flag_tmp = num_flag;
a = scanf("%d", &N);
if (N > MAX_INPUT) return 0;
printf("\n");
for( a=1; a<N; a++)
{
num_flag = update_mask(a, DIGITAL_MASK, &an);
// 0 in a/b/c, not acceptable
if (!(num_flag & 1) || (num_flag&(1<<31))) continue;
ar = N - a;
arn=0;
do{
ar /=10;
arn++;
}while(ar);
/* conditions:
* N = a +b/c, ar = N-a
* 1) bn+cn = DIGITAL_NUM -an
* 2) arn*cn >= bn-1
* 3) bn >= cn
* 4) an, arn, bn, cn > 0
*/
bn = DIGITAL_NUM -an;
ar = N -a;
for(cn=1; cn<=bn; cn++)
{
bn = DIGITAL_NUM - an - cn;
if ((arn*cn >= bn-1)&&(bn >= cn)) // valid
{
// determine the range of c
cmin = find_min(num_flag, cn);
cmax = find_max(num_flag, cn);
for(c=cmin; c<=cmax; c++)
{
num_flag_tmp = update_mask(c,num_flag,NULL);
if (!(num_flag_tmp & 1) || (num_flag_tmp&(1<<31))) continue;
b = c*ar;
num_flag_tmp = update_mask(b,num_flag_tmp,NULL);
if(num_flag_tmp==1)
{
dissamble_num++;
printf("\tNo.%3d: N = %6d, a = %6d, b = %6d, c=%6d\n", dissamble_num, N, a, b, c);
}
}
// break;
}
}
}
// final result
printf("%d\n", dissamble_num);
return 0;
}