UVa 725 除法(Division)
【题目引用】
输入正整数n,按从小到大的顺序排列所有形如abcde/fghij=n的表达式,其中a-j恰好为数字输入0-9的一个排列(可以有前导0),2<=n<=79.
样例输入:62
样例输出:
79546/01283=62
94736/01528=62
【题目分析】
题目中说明了a~j
是一个0~9
的一个全排列,所以这里就隐含着10个数字出现且仅出现一次。那么有没有必要全部枚举呢?这里是不需要的,因为直接可以根据fghij
推出abcde
的值是多少(abcde=fghij*n
),这里就很方便的计算出来的。什么时候可以终止这个枚举的情况呢?也就是说总共的位数超过了10位(一共就10个数字)也就是指分母或分子超过了这个值。
【实际问题】
-
如何确定循环的最小值?从哪开始?
- n最大是两位数,最小是一位数,所以要构成10位数,则需
fghij
是四位数以上 - 从四位数的最小值开始,且四位数中的数字只出现一遍,可见最小值为1234(0被作用于开头作为前导,不然个数不够)
- n最大是两位数,最小是一位数,所以要构成10位数,则需
-
如何确定枚举的最大值
- 这里可以直接定义该值为99999,但是这种情况并不会出现,所以不合理,优化算法不建议此做法
- 根据问题1答案的方式推算,那么可以达到的最大唯一的五位数值应该是98765
-
如何确定序列中数字的唯一性?
- 可以使用map映射,判断对应的键其值是否为1
- 也可以使用数组进行标记,因为数字较小,所以使用数组也蛮简单的。用数组模拟键-值
【代码实现】
#include <iostream>
#include <cstring>
#define MAX 98765
using namespace std;
int visited[10]; //全局数组会默认全为0
bool saveToArray(int value){
//如果value_ed是四位的 那么前导0是必须存在的
if(value < 10000) visited[0] = 1;
while(value){
if(visited[value%10] != 0) return false;
visited[value%10] = 1;
value/=10;
}
return true;
}
int main() {
int n;
while(cin >> n && n){
int mark = 0;
for(int value_ed = 1234; value_ed <= MAX;++value_ed)
{
memset(visited,0, sizeof(visited));
int value_ing = value_ed*n;
//判断该值是否是大于最大值的?
if(value_ing > MAX) continue;
//判断数字是不是唯一出现的
int k = 0;
if(!saveToArray(value_ed)) continue;
//另一个数一定是5位的,不存在超过或者小于
for(;k<5;++k){
if(visited[value_ing%10] != 0) break;
//记得边判断边标记
else if(visited[value_ing%10] == 0) visited[value_ing%10]=1;
value_ing /=10 ;
}
//k以5跳出则说明没有重复
if(k == 5)
{
mark = 1;
if(value_ed < 10000)
printf("%d / 0%d = %d\n",value_ed*n,value_ed,n);
else
printf("%d / %d = %d\n",value_ed*n,value_ed,n);
}
}
if(!mark) puts("No number!");
}
return 0;
}
ps:参考文章中,个人觉得int
范围已经足够以表达数值了,无须定义为Long
类型.
如果上述代码或者描述有错误,可以在评论区评论噢,喜欢的点个赞哈!