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 , 不能通过工程设置而省略常用头文件。
分析:
当看到这个题的时候,一定是使用暴力,但是如何暴力,这是一个问题,需要解决的是如何优化的问题。
我刚开始做的时候,因为“数字1~9分别出现且只出现一次”的这类题目做过好几道,当时觉得一个方法挺好:就是把这9个数字分成题目要求的几份,如本题它需要符合 N = a + b / c 这个式子,所以我可以把9个数字分成三份a、b、c,首先就是循环a,而a的范围肯定是属于[1,N),再就是循环c,为什么先循环c呢,把N = a + b / c这个式子转化一下就是(N - a) * c = b,这样循环a和c就能求出b了。那么接下来就把a,b,c转换到一个字符串数组s中,举个例子比如 a=123 ,b = 456 , c = 789 ,那么数组s为{123456789},之后进行判断,首先判断长度是否为9,然后再进行排序,最后判断是否为123456789的字符串即可。这是一个方法,但是当我做这题时候发现有点不现实。
后来查看别人的代码发现如果循环a的时候将a进行check的一下,判断a是否有0,是否有重复,然后再进行第二层的循环,这样时间复杂度将大大减少。
int check(int n)
{
do{
flag[n%10]++;/将n每一位的出现的次数记录在flag数组中。
}while(n/=10);
if(flag[0]!=0) return 0;//如果n中有0,则返回0
for(int i=1; i<=9; i++)
{
if(flag[i]>1) return 0;//出现重复的数,返回0
}
return 1;
}
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char s[10];
int N,a,b,c;
int flag[10];
int backup[10];
int checkAll()
{
sprintf(s,"%d%d%d",a,b,c);
if(strlen(s)!=9) return 0;
int t=1;
sort(s,s+9);
for(int i=0; i<9; i++)
{
if(s[i]!='0'+t)
return 0;
t++;
}
return 1;
}
int check_s(int n)
{
do{
flag[n%10]++;
}while(n/=10);
if(flag[0]!=0) return 0;
for(int i=1; i<=9; i++)
{
if(flag[i]>1) return 0;
}
return 1;
}
int main()
{
scanf("%d",&N);
int k=0;
for(a=1; a<N; a++)
{
memset(flag, 0, sizeof(flag));
if(!check_s(a))
continue;
for(int i=0; i<=9; i++)
{
backup[i] = flag[i];//记录这时的flag,已将a中的每个数记录好
}
for(c=1; c<100000; c++)
{
for(int i=0; i<=9; i++)
{
flag[i]=backup[i];//清除到之前记录时的flag
}
b = (N-a)*c;
if(!check_s(b)||!check_s(c)) continue;
if(checkAll())
{
k++;
}
}
}
printf("%d",k);
return 0;
}
这样,复杂度将大大减小。