初学acmer--《算法竞赛经典入门》读书笔记(暴力专题)P182

题目:
Division


Write a program that finds and displays all pairs of 5-digit numbers that between them use the digits 0 through 9 once each, such that the first number divided by the second is equal to an integer N, where 2 ≤N≤79. That is, 


abcdefghij=N


where each letter represents a different digit. The first digit of one of the numerals is allowed to be zero.
Input


Each line of the input file consists of a valid integer N. An input of zero is to terminate the program.


Output


Your program have to display ALL qualifying pairs of numerals, sorted by increasing numerator (and,of course, denominator).


Your output should be in the following general form: 
xxxxx / xxxxx = N 
xxxxx / xxxxx = N 

.


In case there are no pairs of numerals satisfying the condition, you must write ‘There are no solutions for N.’. Separate the output for two different values of N by a blank line.


Sample Input


61 
62 
0


Sample Output


There are no solutions for 61. 
79546 / 01283 = 62 
94736 / 01528 = 62


输入正整数n,按从小到大的顺序输出所有形如abcde/fghij = n的表达式,其中a~j恰好为数字0~9的一个排列(可以有前导0),2≤n≤79。


样例输入:
61
62
0
样例输出:
There are no solutions for 61.
 
79546 / 01283 = 62 

94736 / 01528 = 62



分析:这个可以用暴力求解法,但是如何暴力,暴力到啥程度值得注意。若是单纯地枚举0~9的所有排列就显得太繁琐了。通过对题目中abcde/fghij = n进行分析,可以移项得到n*fghij=abcde,故而只需枚举fghij就可以算出abcde,然后判断是否所有数字都不相同即可。不仅简单,而且枚举量从10!=3628800降低到不到一万,而且只要abcde和fghij只要超过100000就可以终止枚举。好了,算法分析出来了,那么就要写代码了。写代码主要有两个问题,一是如何将枚举的数放入数组中,二是如何判断数组是否有重复的数。下面贴代码,有两个版本,可以进行一下比较它们对以上两个问题的解决策略。

第一种(繁琐版)

#include
#include
int judge(char s[])
{
for(int i=0;i<9;i++)     //②
{
for(int j=i+1;j<10;j++)
{
if(s[i]==s[j])
return 0;
}
}
return 1;

}
int main()
{
int n,fg=1;
while(scanf("%d",&n)&&n!=0)
{
if(fg)
fg=0;
else printf("\n");
int flag=0;
char s[10]={0};
for(int i=1234;i<98765;i++)

        int t=9;
        int m=i;
   while(m)      //①
   {
    s[t--]=m%10;
    m/=10;
    }

int k=n*i;
if(k>=100000)
break;
else 
{
int q=4;
while(k)
{
s[q--]=k%10;
k/=10;
}
//puts(s);
if(judge(s))
{
flag=1;
for(int j=0;j<10;j++)
{
if(j==5)
printf(" / ");
printf("%d",s[j]);
}
printf(" = %d\n",n);
}
}

if(!flag)
printf("There are no solutions for %d.\n",n);
}
return 0;
}


第二种(改进版)

#include  
#include  
using namespace std;  
  
bool num[10];  
char str[10];  
  
bool check(int a,int b){  
    sprintf(str,"%05d%05d",a,b);                 // ① 
    memset(num,false,sizeof(num));             //②
    for(int i=0;i<10;i++){  
        if(num[ str[i]-'0' ])   return false;  
        num[ str[i]-'0' ]=true;  
    }  

    return true;  
}  
int main(){  
    int n;  
    bool first=true;   
    while(scanf("%d",&n) && n){  
        bool ok=false;  
        if(first)   first=false;  
        else        printf("\n");  
        for(int i=1234;i<=98765;i++){  
            if(i%n==0){  
                if(check(i,i/n))  
                    ok=true,printf("%05d / %05d = %d\n",i,i/n,n);  
            }  
        }   
        if(!ok) printf("There are no solutions for %d.\n",n);  
    }  
    return 0;  
}  
PS:①对于“如何将枚举的数放入数组中”这个问题,第一种方法是通过"取余相除过河拆桥”法将每一位的数一一放入数组中,而第二种方法却是通过函数sprintf(s,“%d”,x)实现的,简洁有效!

关于“如何判断数组是否有重复的”这个问题,法一通过两个for语句简单排查,而法二则是通过构造一个对应的“记录数组”,(不合适应用于存在递归的函数中)先是将这个“记录数组”初始化为零,然后只要出现一个数,就将以这个数为下标的数组单元赋值为一,每次操作都先检查数组单元是否为一,若是,则有重复,否则就将其赋值为一


总结:即使采取暴力法求解问题,对问题进行一定的分析往往会让算法更加简洁、高效。另外,在枚举复杂对象之前,先尝试着枚举一些相对简单的内容,如整数、子串。

你可能感兴趣的:(acm)