带分数问题

带分数问题

来自网友这里提问的问题
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 >0

step1:得到一个合适的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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值