蓝桥杯-带分数(C语言)

9. 标题:带分数
可以表示为带分数的形式:100 = 3 + 69258 / 714

还可以表示为:100 = 82 + 3546 / 197

注意特征:带分数中,数字1~9分别出现且只出现一次(不包含0)。

类似这样的带分数,100 有 11 种表示法。

题目要求:
从标准输入读入一个正整数N (N<1000*1000)
程序输出该数字用数码1~9不重复不遗漏地组成带分数表示的全部种数。
注意:不要求输出每个表示,只统计有多少表示法!

例如:
用户输入:
程序输出:

再例如:
用户输入:
程序输出:

解题思路(全排列)

如果对全排列算法不同可以看看本人的另两篇博客。

[非递归全排列]https://blog.csdn.net/qq_42552533/article/details/88609886
[递归全排列]https://blog.csdn.net/qq_42552533/article/details/88606550

将“123456789”全排列,然后用a,b,c划分,我们这里主要讲要怎么划分:

我们规定a在最前面,b在中间,c在尾部

这样我们可以知道a的头部就是list的第一位list[0],a的尾部就是b的头部(不能确定)

b的尾部则是c的头部(也不能确定),但是我们知道c的尾部一定是list[8]。

然后又有number = a + b / c 等式成立。变形一下:b = (number - a) * c。

好了,注意,根据乘法原理我们可以推出b的尾部数字(设为BLast)。

BLast=((number-a)*list[8])%10; //确定b最后一个数字。

最后为了程序优化,我们剪掉一些不可能的情况。

下面我们对a,b,c的区间进行分析:
number = a + b / c
a为带分数的左边,(1<= a <= 1000,000)这个范围没错吧?1000,000为题目规定范围。我们用for循环人为设定a区间的尾部。

b为带分数的上部,这里隐含条件(b % c == 0)必须为整数。所以b>=c这点是必须的,由于我们划分的是区间,

所以区间长度就能近似的表示数值的大小了,区间越长,说明数值位数越多,肯定值越大,所以b的区间长度就必须不小于c的区间长度。

也就是说a后面剩下的list长度b要占一半以上。因为b的最后一位我们已经算出来了,所以我们需要匹配最后一位的位置,就能划分出a,b,c区间。

说了这么一大堆,我自己都搞晕了。放个例子出来溜溜,还是以上面那个排列来说明。

示例:3+69258 / 714 (number = 100)

abc = 369258714

我们是这么来划分的:
第一步:人为划分a(如:(0,1)),即a = 3

第二步:计算出bLast=((number-a)*list[8])%10 = ((100 - 3) * 4)%10 = 8

第三步:a = (0,1)那么bc则是(2,8),那么b的最后一位我们从(8-2)/2 = 3,也就是从list[3] = 2开始找起(直接跳过前面不可能情况) 369-258714

第四步:判断BLast == list[i],当找到了b的尾部的时候,我们就开始检查组合的合理性:它将满足条件number == a+b/c且b%c==0(如果合理)

第五步:如果合理则将情况种数+1同时跳出for循环,进入递归找下一组排列。

如果不合理,则将a划分区间长度+1,a = (0,2),即a = 36,然后再确定b的尾部BLast…

下面是一个简单的图分析:
在这里插入图片描述

程序代码如下:

#include<stdio.h>
#include<string.h>

int count = 0 , number = 0 , x = 0;

/*交换*/
void swap(int *a,int *b )
{
	int temp = *a;
	*a = *b;
	*b = temp;
}

/*将数字区间转换成数字*/
int GetNum(int arry[],int f,int r)
{
	int num=0;
	for(int i=f;i<=r;i++)
	{
		num = arry[i] + num * 10;
	}
	return num;
}

//进行全排列并对每种排列结果进行处理 
void Allarrange(int arry[],int s,int length)
{
	if( s == length )
	{
		int a,b,c,BLast;
		for(int i = 0 ; i < x ; i++ )
		{
			a = GetNum(arry,0,i);
			BLast = ((number-a)*arry[8])%10; //找到b尾部的值 
			
			for(int j=i+(8-i)/2 ; j < 8 ; j++)
			{
				if(BLast == arry[j])         //找到b尾部
				{
					b = GetNum(arry,i+1,j);  //将arry数组中的[i+1-j]转化为数字,赋值给b
					c = GetNum(arry,j+1,8);  //将arry数组中的[j+1-8]转化为数字,赋值给c
				
					if( b % c == 0 && a + b / c == number )//判断合理性 
					{
						printf("%d=%d+%d/%d\n",number,a,b,c);
						++count;
					}
					break;
				}
			}
		}
	}
	else
	{
		for(int i=s;i<=length;i++)
		{
			swap(&arry[s],&arry[i]);
			Allarrange(arry,s+1,length);
			swap(&arry[s],&arry[i]);
		}
	}
}

int main()
{
	int arry[]={1,2,3,4,5,6,7,8,9};
	int len=sizeof(arry)/sizeof(arry[0]);
	scanf("%d",&number);
	int temp =number;
	while(temp!=0)
	{
		x++;             //统计输入number的位数 
		temp /= 10; 
	}
	Allarrange(arry,0,len-1);
	printf("%d",count);
	return 0;
}

运行截图:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值