标题:带分数
100 可以表示为带分数的形式:100 = 3 + 69258 / 714
还可以表示为:100 = 82 + 3546 / 197
注意特征:带分数中,数字1~9分别出现且只出现一次(不包含0)。
类似这样的带分数,100 有 11 种表示法。
题目要求:
从标准输入读入一个正整数N (N<1000*1000)100 0000
程序输出该数字用数码1~9不重复不遗漏地组成带分数表示的全部种数。
注意:不要求输出每个表示,只统计有多少表示法!
例如:
用户输入:
100
程序输出:
11
再例如:
用户输入:
105
程序输出:
6
资源约定:
峰值内存消耗 < 64M
CPU消耗 < 3000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include
提交时,注意选择所期望的编译器类型。
解题思路:
该题的条件是 (x+(y/z)==n)&&(y%z==0)并且x,y,z是1~9九个数字组成的,每个数字只能用一次。
所以就可以将x,y,z三个数放在一起看成一个九个数的序列,对该序列进行全排列,每次全排列划分不同的数给x,y,z,如果符合条件则计数器+1。
这样写太耗时,可能后台评测数据太水,勉强没有超时。
AC代码:
#include
#include
using namespace std;
int main()
{
int a[9]={1,2,3,4,5,6,7,8,9};
int n,cnt=0;
scanf("%d",&n);
do
{
int x=0,y=0,z=0;
for(int i=0;i<7;i++)
{
x=x*10+a[i];
y=0;
for(int j=i+1;j<8;j++)
{
y=y*10+a[j];
z=0;
for(int k=j+1;k<9;k++)
{
z=z*10+a[k];
}
//printf("x===%d y===%d z===%d\n",x,y,z);
if((x+(y/z)==n)&&(y%z==0))
{
cnt++;
}
}
}
}while(next_permutation(a,a+9));
printf("%d\n",cnt);
return 0;
}
以下是关于这道题更完美的解法:
https://blog.csdn.net/jopus/article/details/18998403
其中关于全排列的一部分思想:
根据题目的要求,输入一个数字,需要将这个数字化成带分数,且包含(0-9)所有数字(不重复)。
这里我们假设输入的数字为number,划分好的带分数为a+b/c,它将满足条件number == a+b/c且b%c==0,同时abc包含所有(0-9,且不重复)。
请注意最后最句话,abc包含所有(0-9,且不重复),我们再看题目给我们的那个满足条件的数字:
3+69258/714 这里a = 3, b = 69258, c = 714 ;则abc = 369258714(这个数字数list的一种排列方式)。
所以我们想到一种方法:先求出list的一种排列,然后对这个排列进行a,b,c划分处理,如果满足条件
(条件:number == a+b/c且b%c==0,同时abc包含所有(0-9,且不重复))则将记录种数+1 。
上面已经分析了,将“123456789”全排列,然后用a,b,c划分,我们这里主要讲要怎么划分:
还是用上面那个排列:abc = 369258714
我们规定a在最前面,b在中间,c在尾部(因为对list进行了全排列每种情况都会出现,所以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最后一个数字。
最后为了程序优化,我们剪掉一些不可能的情况。
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.................
下面是一个简单的图分析:
通过上面分析我们可以优化全排列算法,更快的找到分子取值的位置节省时间,建议看原文,了解全排列的手动实现的过程。
https://blog.csdn.net/jopus/article/details/18998403