不懂算法之带分数


题目描述

标题:带分数
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
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include <xxx>, 不能通过工程设置而省略常用头文件。
提交时,注意选择所期望的编译器类型。


哎,说来也是惭愧,今天去参加校内蓝桥杯选拔赛,编程题最后一题就是这个,前面几道全是用穷举法做,这一道也很自然的想到用穷举法。但是粗略一想,我想先把这个带分数表达式先搞成字符串的形式,但是深入一想。。那尼玛是个无底洞,,果断放弃。之后观察第一个整数和分数的分子分母有什么关系么。。果然,没有,至少我看不出来。怪自己算法思想不够,数学功底欠缺,没有考虑分数的分子分母的位数在N(N<1000*1000&数字1~9分别出现且只出现一次)的条件下是有个上界的。


如果分母是一个五位数,那么分子也必须至少为五位数,因为如果分子不是五位数,那么这个分数一定是小于1的,那也就不可能是这个解了。既然分子分母都是五位数,又只有9个数字,那么分母不可能是五位数。所以这个分数分母的位数最多是4位,最少是1位。


对于第一个整数,由于N<1000*1000,至少存在如下关系不等式:

1 <= 这个整数的位数 <= N的位数;

如果这个整数的位数大于了N的位数,则肯定不是这一种可能了。


既然确定了整数和分母,分子其实可以通过反表示法来表示了:

a + b / c = n;    --->     b = (n - a) * c;


设该等式形式为: 

    left + up / down = n;

核心代码:

        for(down = 1; down < 10000; down++)
{
for(left = 1; left < Pow(10, n_bit); left++)
{
up = (n - left)*down;
   if(Check(left, up, down))
    {
    count++;
    printf("%ld + %ld / %ld\n", left, up, down);
   }    
}
}

解释:

外循环对分母从1到10000(分母是不会超过四位数的)循环,内循环对整数从1到10的n的位数次方(上面分析过整数的位数不会超过n的位数),那么整数和分母的值现在已经是确定了的,所以分子的值也是确定了。剩下的就只是判断这三个数1~9这九个数字是不是有且仅出现了一次即可,交给Check函数来做(如果满足要求,返回1,;否则返回0),如果满足了要求,那么计数变量count就自增1,然后输出这种情况下的表达式。


下面附上完整代码:

#include<stdio.h>
int Num_Bit(long x);
long Pow(int x, int y);
int Check(long left, long up, long down);
int main(void)
{
	int n;
	int count = 0;
	long up, left, down;
	int n_bit, up_bit, down_bit, left_bit;
	
	printf("Enter n: ");
	scanf("%d", &n);
	n_bit = Num_Bit(n);
	for(down = 1; down < 10000; down++)
	{
		for(left = 1; left < Pow(10, n_bit); left++)
		{
			up = (n - left)*down;
		    if(Check(left, up, down))
    		{
		    	count++;
		    	printf("%ld + %ld / %ld\n", left, up, down);
		    }    
		}
	}
	
	printf("count: %d\n", count);
	
	return 0;
}

int Num_Bit(long x)
{
	int bit = 0;
	
	while(x)
	{
		x = x/10;
		bit++;
	}
	
	return bit;
}

long Pow(int x, int y)
{
	int i;
	long z = 1;
	
	for(i = 1; i <= y; i++)
	    z *= x;
	    
    return z;
}

int Check(long left, long up, long down)
{
	int a[9] = {0};
	int t;
	
	while(left)
	{
		t = left%10;
		if(t == 0)
		    return 0;
		a[t-1]++;
		left = left/10;
	}
	while(up)
	{
		t = up%10;
		if(t == 0)
		    return 0;
		a[t-1]++;
		up = up/10;
	}
	while(down)
	{
		t = down%10;
		if(t == 0)
		    return 0;
		a[t-1]++;
		down = down/10;
	}
	for(t = 0; t < 9; t++)
	{
		if(a[t]!= 1)
		    return 0;
	}
	
	return 1;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值