ACM 竞赛第六题(用时4小时) (2011-3-9 10:04)

题目要求:

输入一个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);
  }
 }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ACM的,你懂得 ACM过程中的一些小技巧。 1.一般用C语言节约空间,要用C++库函数或STL才用C++; cout、cin和printf、scanf最好不要混用。 大数据输入输出最好不要用cin、cout,防止超。 2.有候int型不够用,可以用long long或__int64型(两个下划线__)。 值类型表示值介于 -2^63 ( -9,223,372,036,854,775,808) 到2^63-1(+9,223,372,036,854,775,807 )之间的整数。 printf("%I64d",a); //__int64 一般VC编译器使用(虽然有的OJ用g++,但是动态链接库用的windows的,所以要用%I64d输入输出) printf("%lld",a); //long long 一般g++编译器使用 3.OJ判断是只看输出结果的,所以不要要多余的提示输出。 所以大部分处理一组数据后可以直接输出,就不需要用数组保存每一个Case的数据。 while(case--) { scanf(...); ...... printf(...); } 4.纯字符串用puts()输出。 数据大最好用scanf()、printf()减少间。 先用scanf(),再用gets()会读入回车。所以在中间加一个getchar(); scanf("%c%c",&c1,&c2)会读入空格;建议用%s读取字符串,取第一个字符。 5. 读到文件的结尾,程序自动结束 while( ( scanf(“%d”, &a) ) != -1 ) while( ( scanf(“%d”, &a) ) != EOF) while( ( scanf(“%d”, &a) ) == 1 ) while( ~( scanf(“%d”, &a) ) ) 读到一个0,程序结束 while( scanf(“%d”, &a) , a) while( scanf(“%d”, &a)!=EOF && a) 读到多个0,程序结束 while( scanf(“%d%d%d”, &a, &b, &c), a+b+c ) //a,b,c非负 while( scanf(“%d%d%d”, &a, &b, &c), a|b|c ) 6.数组定义int a[10] = {0};可以对其全部元素赋值为0; 数组太大不要这样,防止CE。 全局变量,静态变量自动初始化为0; 函数中定义的变量存储在栈空间中,数组太大需要定义为全局变量(存储在堆空间中)。 7.有很多数学是有规律的,直接推公式或用递归、循环。 8.圆周率=acos(-1.0) 自然对数=exp(1.0) 9.如果要乘或除2^n,用位移运算速度快。a>>n;a<b?a:b; } int gcd(int m,int n) { return n?gcd(n,m%n):m; } int abs(int a) { return an; } sort(a,a+n,cmp); 14.有的数据范围小但是计算量大可以用打表法 先把结果算出来保存在数组里,要用时直接取出来。 15.浮点数比较最好控制精度 #define eps 1e-6 fabs(a-b)<eps 16.有些字符串与整型的转换函数是非标准的 可以使用sscanf()和sprintf()代替 sscanf(s,"%d",&n);//从字符串s中读入整数n sprintf(s,"%d",n);//将n转换为字符串s

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值