PAT 1038 Recover the Smallest Number

1038 Recover the Smallest Number
大意:输入n个数字串, 输出这些串拼接成的最小的数字
32 321 3214 0229 87 能拼出的最小整数
229 321 3214 32 87

一开始的思路想的太简单,以为把数字串排个字典序即可。
试了一下就知道,按这个思路拼出来的是 229 32 321 3214 87, 显然不对。

然后我的思路就开始歪了。想着排序之后得到一个最小的估计值。再dfs搜索,遍历各种可能组合,用估计值去剪枝。写到一半发现, 判断两个串谁大谁小很简单,拼起来看看谁在前,谁在后会结果比较小即可。
例如 32 3213拼接 得到 323213 和 321232,明显第二个小,所以 3213在前会比较好。

问题就变成:为排序设计一个合适的比较函数即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
struct segment{
    char s[17]={0}; //能容纳两个串,在 operator< 中只需复制2次串就行了。避免用临时数组复制4次字符串 
    char len=0;
    bool operator<(  segment& a ) 
    {
        strcpy( s+len, a.s);
        strncpy( a.s+a.len, s, len); //按两种顺序拼接字符串 
        bool ret = strcmp( s, a.s )<0;
        s[len]=a.s[a.len]=0; //恢复串结尾 
        return ret;
    }
};

int main()
{
    int n, digit=0;
    scanf("%d", &n);
    vector<segment> m(n);
    segment tmp;
    for( int i=0; i<n; i++ )
    {
        scanf("%s", tmp.s);
        tmp.len = strlen( tmp.s );
        digit += tmp.len;
        m[i] = tmp ;
    }
    sort(m.begin(), m.end());
    auto it = m.begin();
    do{
        sscanf( it->s,  "%d", &n ); //跳过0的segment,单独输出第一个非0segment
        ++it;
    }while( n==0 && it!=m.end());  //万一有全0的情况 
    printf("%d", n );

    char *result = new char[digit+1]();
    char *p=result;
    for( ;it!=m.end(); ++it )
    {
        strcat(  p, it->s ); //p指向目的串的末尾,减少strcat的遍历 
        p += it->len;
    }
    printf("%s", result);
}
//7 32 321 3214 0229 87 000 0000 

这里之所以不用string是为了避免很多不必要的复制,以及精确控制串的占用的空间。
如果用string, 比较函数就会写成 a+b < b+a, 这里会构造两个临时string对象,复制ab 和 ba,随后再析构。
而我的比较函数之中,只需要一半的复制量。 并且不需要构造析构。

从结果来看,这份代码只需要 11ms 左右,内存700kb,
用了string的话,就要 50ms,900k。

使用c++ 中的对象,常会遇到很多临时对象的复制构造析构。
有些是可以避免的(通过定义良好的swap操作,移动复制,甚至特定专门的)但是这些会付出额外思考精力,牺牲可读性,通用性等等。
stl的string库针对通用目的设计的,确实很方便,但是也带来很多临时对象的复制构造问题,针对题目的特定情况,定制操作可以锻炼自己识别不必要复制的能力。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值