[蓝桥杯2013省 B]带分数解题思路

题目解析

题目要求将1——9这九个数字都用来组成一个整数加一个分数的形式,且每个数字只能用一次,最终统计出相加结果等于给定数N的表示法有多少种。

拿到题的第一时间,脑子里就会想到本题需要经过的几个步骤:首先枚举出将九个数字分成一个整数和一个分数的各种排列,然后计算每种排列的结果,再作判断看是否等于N,如果是则种数加一,最后输出种数。

那么,如何将每一种排列方式都枚举出来呢?

我是先将这九个数字全排列,得到九的阶乘个结果,对这些结果进行分段,分成整数,分子,分母三段,用a,b,c表示,分别在这九个数的前面,中间,结尾。(全排列的方法为字典序法,为了避免篇幅过长就不在这赘述了,大家可以自行搜索学习,C++中可以引 #include <algorithm>直接调用函数next_permutation,将数组元素排列为按字典序法的下一组排列)。比如123456789可以分成a:12,b:3456,c:789这三段。

在这里放一个式子:BLast=((number-a)*list[8])%10(由number=a+b/c可得)

这个式子是用于寻找b段的尾数字BLast的,number是指输入的九个数字组成的数,list[8]是指c段的尾数,也是在本次排列中这九个数字的最后一位。

这里有个难点,之所以用((number-a)*list[8])%10就能取到b的尾数字,是因为((number-a)*list[8])%10和(number-a)*c)%10的结果是一样的,因为两个数相乘所得结果的个位数就等于这两个数的个位数相乘,比如(521*1314)%10就等于1*4=4。

得到BLast后,先设置a段的长度为一,即list[0]。显然本题中还有几个隐藏条件:b%c==0,b>=c,a的值小于number,位数小于等于number。

所以剩下的八个数字,b至少占四个,也就是说我们可以从第五个数字开始,通过判断语句判断list[i]是否等于BLast以查找BLast的位置,一旦确认b的尾数字的位置就可以表示出abc三段,从而表示出a+b/c的值,判断是否等于number,是则总数加一,然后a的位数加一进入下一循环,当a的位数无法增大时,开始找下一个排列。

再说一下如何表示出abc。由于各段的位数一直在变化,难以直接用数字乘以十的N次方的方式表示出abc的具体值,所以使用了循环的方式,用每一段的第一个数作为初始的abc,每一次循环都乘以十并加上下一位数,各段的位数减一作为循环次数。比如计算得a为两位,b为四位,c为三位,就可以将arr[0],arr[2],arr[6]分别设为初始的abc的值,a的初始值经过循环被乘十加arr[1],得到a=arr[0]*10+arr[1],循环共进行(2-1)次,bc同理。

代码如下:

//https://www.luogu.com.cn/problem/P8599
//上方为洛谷题目链接
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
# include <iostream>
# include <algorithm>
#include<math.h>
using namespace std;
int main()
{
	int i,j,k,h,m,n,number,Num= 0;
	int num = 1;
	int arr[9] = { 1,2,3,4,5,6,7,8,9 };
	scanf("%d", &number);
	n = number;

		while (n /= 10)
		{
			num++;
		}
	/*sort(arr, arr+9);*///升序排列,第二参数为数组末元素的下一位的地址。
	/*next_permutation;*/// 按字典序排列输入数组的顺序,将其变为原数组的下一位。*/
	/*for (i = 0; i < 9; i++)
	{
		printf("%d ", arr[i]);
	}*/
	do
	{
		for (i = 1; i <= num ; i++)
		{
			int a = 0, b = 0, c = 0;
			int BLast = 0;
			int prove = 0;
		     a = arr[0];
			for (j = 1; j < i; j++)
			{
				a = a*10 + arr[j];
			}//求出当变量的位数为i时的值。
			if (a < number)
			{
				BLast = ((number - a) * arr[8])%10;
				for(k = ((9-i)-(9-i)/2-1)+i;k<8;k++)//本题隐藏条件:b%c==0。
				{
					if (arr[k] == BLast)
					{
						b = arr[i];
						c=arr[k+1];
					  for (h = 1; h <k+1-i; h++)
					  {
						b = b * 10 + arr[h+i];
					  }
					  for (m = 1; m < 8-k; m++)
					  {
						c = c * 10 + arr[m+k+1];
					  }
					  prove = 1;
					  break;
                    }
				}
				if (prove == 1)
				{
					if (b % c == 0)
					{
						if ((a + b / c) == number)
							Num++;
					}
				}
					
			}
			else
				break;

			
		}
	}
//用do_while是为了让初始排列也参与操作
	while (next_permutation(arr, arr + 9));
//如果函数可以将对象重新排列为词典上更大的排列,为true。否则,该函数将返回以指示排列不大于前一个排列,而是尽可能低的排列(按升序排序),为false
	printf("%d", Num);
		return 0;
}

最后也是全测试点顺利通过

以上内容仅为我自己在学习后的解题思路,如有错误欢迎各位大佬评论区指正,大家一起进步!!

  • 33
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值