zoukankan      html  css  js  c++  java
  • 数字接力赛(FZU-1593)

    数字接力赛(FZU-1593)  福州大学第五届程序设计竞赛

    时间限制 1000ms 内存限制 32768KB

    问题描述

    给定两个数字,可将它们拼接在一起形成一个新的数字,称之为数字接力。

    现在进行数字接力赛,给定n个数,看哪种拼接方式形成的数字最大。例如1,2,3三个数字,六种可能的拼接结果是:123,132,213,231,312,321。容易发现最大的数字是321。

    输入

    本题有多组输入数据,你必须处理到EOF为止。

    每组数据第一行是一个正整数$n$($1≤n≤100000$)。接下来$n$行每行一个非负整数$m$ ($0≤m<2^{31}$)。

    输出

    输出一行,表示由这$n$个数拼接而成的最大数字。

    样例输入

    3
    1
    2
    3

    样例输出

     321

    这个题要求将给定的数拼起来,而考虑到每个数字都有可能达到32位有符号整数的大小,因此不大可能直接使用int数据类型作为结果进行存储,即无法直接进行数字大小的比较。所以返朴归真,考虑如何进行数字大小的比较。到问题中是由相同的数按照不同排列方式拼接而成,因此最终得出的数应该长度相同。设一个十进制数$A,B$分别由$n$位数码$A_1,A_2,...A_k,...A_n$,$B_1,B_2,...B_k,...B_n$构成,于是比较数$A$和数$B$的大小,即从最高位往最低位依次比较$A_i$和$B_i$。如果$A_1=B_i$那么比较$A_{i+1}$和$B_{i+1}$,直到比较出大小为止。因此问题就转化为字符串比较的问题了。其次就是去解决如何拼接的问题。暴力穷举显然是不可行的,因为$n$个数字的全排列数为$n!$个,显然时间是不允许的。考虑我们需要最大的数,因此要求从最高位到最低位(从左往右)尽可能的最大,因此不难想到使用一个排序了的数组依次从中取出最大元素构造答案。单纯最大显然不行,考虑这样一组数据:$s_1=51$,$s_2=52$ ,$s_3=512$,直接用ASCII序对字符串排序,得到的是$s_2>s_1>s_3$,即$5251251$,但是最大的数$5251512$。问题出现在相同前缀但是长度不一样的字串上。对于这些串直接用ASCII码进行比较,必然是长度大的串较大;而实际上我们需要保证拼接后的串进行比较,因此可以自定义一个比较函数,在比较$s_1$和$s_2$时,对$s_1+s_2$和$s_2+s_1$进行比较,从而决定在输出序列中的排序时的先后位置。对于前缀不同的字串,这种比较显然也是正确的。当从2个串扩展到多个串,我们便需要证明排序后的输出是正确的。下面使用了循环不变式(类似于数学归纳法,详细描述见《算法导论》)来证明算法的正确性:

    1)当$n=1$时,只有一个串(原题中是数字,但是这里当作串来处理),直接输出显然是最大的。

    2)当$n=k$时,假设这时候得到的数$A$,其数组为$A[1..k]$是按照我们预想的方法排好序的,即$A[1]A[2]..A[k]$是前$k$个元素构成的最大数(这里假设$A[k]$代表的是数中的一串数字),满足$A[1]>A[2]>...>A[k]$,那么当$n=k+1$时,对于任意的$i≤k$都有$A[k+1]<A[i]$成立,如果这时$A[k+1]$处于$1..i$中的任意位置$p$上,而得到新的数$A′$,那么采用朴素比较到第$p$个数时(对应数组的第$r$个元素,即数字从左往右的第$r$位,其中$r = sum_{n=1}^pA[n]. m{length}$,这时显然会有$A′<A$。因此证明了$n=k+1$时也成立。

    3)当循环结束时,数组中的所有元素全部输出,已输出的元素序列构成了最大数,因此得到了正确的答案。

    这样就得到了这个问题的解决方法:先对输入的$n$个串按照特定方式排序,然后按照排序结果输出。整个算法的复杂度为$O(n{ m lg}n)$。排序可以调用STL的sort函数,其中只需自定义一个比较函数mycmp.稍微提及一下sort的函数前两个参数为随机迭代器,直接使用数组下标也是可以的。我一开始试图使用重载<运算符,但是直接使用string也不可行,便定义了一个只有string成员的struct,对struct重载<运算符,结果TLE了。后来发现直接使用STL基本属于常数临界的状态,而struct在编译器不优化的情况增加了一次寻址操作,也就是增加了常数。不过STL的string类确实简化了操作,如果不用string类,显然使用strcat后再用strcmp写起来非常麻烦。我的C++实现如下:

     1 #include<iostream>
     2 #include<string>
     3 #include<algorithm>
     4 using namespace std;
     5 bool mycmp(string a, string b)
     6 {
     7     return ((a + b) > (b + a));
     8 }
     9 string src[100000];
    10 int main()
    11 {
    12     int n;
    13     while (cin >> n)
    14     {
    15         for (int i = 0; i < n; i++)
    16             cin >> src[i];
    17         sort(src, src + n, mycmp);
    18         for (int i = 0; i < n; i++)
    19             cout << src[i];
    20         cout << endl;
    21     }
    22     return 0;
    23 }

    p.s.leaderboard的93ms的代码应该是$O(n)$的,确实想不明白了,我很好奇如何实现的。。。。。。

  • 相关阅读:
    操作数组可以通过Array这个类来操作(不需要考虑数组的类型!!!)
    Servlet------>jsp自定义标签SimpleTag(jsp2.0以后的方法,1-5已经淘汰了)
    Servlet------>jsp自定义标签5(标签体内容改为大写)
    Servlet------>jsp自定义标签(JSPTAG接口)
    Servlet------>jsp自定义标签4(重复标签体)
    Servlet------>jsp自定义标签3(不显示余下jsp内容)
    模块学习
    正则表达式与re模块
    模块
    迭代器与生成器
  • 原文地址:https://www.cnblogs.com/ggggg63/p/6690805.html
Copyright © 2011-2022 走看看