题目解析
题目要求将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;
}
最后也是全测试点顺利通过
以上内容仅为我自己在学习后的解题思路,如有错误欢迎各位大佬评论区指正,大家一起进步!!