题目要求:
输入一个T,则T线跟进,紧接着输入一个数num(10<num<10的1000次),保证输入不会有0,寻找一个最小的比num大的数
数的要求是:num每一位的乘积和这个数的每一位的乘积一样大。
样例输入:
3
12
19
222
样例输出
21
33
241
普通思路——穷举:无奈10的1000次太大,根本没办法举得尽;
优化思路——拆分:从个位到最高位每两位做,不行就做三位,但是当不能被64位整形所替代的数字也是没办法的,而且即使可以,也必定超时!
最终思路——记录、查找、排序:取出当前的数和数组里的数一一做过来,只要找到,替换这个数和数组的数所指的下标的数,然后最高位不变,后面的数排序处理。最后跳到主函数判断有没有做过交换,如果没有,则在前面输出1接着输出这个数,否则就直接输出这个数。注:(这个数组时用来记录不同的数字的下标,初始值全部是-1,当一个数和前面的数全部不符合要求的时候,就把这个数放到数组里)
代码:(建议理解了思路之后看代码,不然会一头雾水)
#include<stdio.h>
#include<string.h>
char a[1005];//由于是超大数,只能用数组来表示,字符变数字只需-48即可
int flag,len,link[10],num;
void start(){
int i;
for(i=0;i<10;i++)
link[i]=-1;
flag=0;
}
int find2(int n,int m){
int i;
for(i=n*10+m+1;i<100;i++){
if((i%10)*(i/10)==n*m){
return i;
}
}
return 0;
}
void find(){
int i,j,min,minj=-1,tempi,tempj,tempk;
char temp;
start();//初始化数组和flag
for(i=len-1;i>=0;i--){
if(i==len-1){//如果是第一个,把数放到link数组里
link[a[i]-48]=i;
}else{ //否则,从1到10判断link里有没有这个数,如果有则找符合要求的数(片段) find2(int n, int m)
min=101;
for(j=1;j<10;j++){
if(link[j]>0){
num=find2(a[i]-48,j);
if(num>0 && num<100){
if(num<min){
min=num;
minj=link[j];
}
flag=1;
}
}
}
if(flag){//如果找到这个数(片段)
a[i]=min/10+48;
a[minj]=min%10+48;//就把这个数替换原来的两个数字
for(tempi=i+1;tempi<len-1;tempi++){//最高位不变,剩下的数排序保证数最小
tempk=tempi;
for(tempj=tempi+1;tempj<len;tempj++){
if(a[tempk]>a[tempj])
tempk=tempj;
}
if(tempk!=tempi){
temp=a[tempi];a[tempi]=a[tempk];a[tempk]=temp;
}
}
return ;
}else{//如果找不到这个数(片段),就把当前的数字放到link[]数组里
link[(int)a[i]-48]=i;
}
}
}
}
int main()
{
int t,i,j,k,temp;
scanf("%d",&t);
getchar();
while(t--){
gets(a);
len=strlen(a);
find(); //查找这个数
if(flag){//找到了,直接输出
puts(a);
}else{//找不到,前面加个1排序输出
for(i=0;i<len;i++){
k=i;
for(j=i+1;j<len;j++){
if(a[k]>a[j])
k=j;
}
if(k!=i){
temp=a[i];a[i]=a[k];a[k]=temp;
}
}
printf("1%s\n",a);
}
}
}