[ACM] 经验总结(一)

1>  I/O效率问题

一篇文章中指出——getchar()/putchar()快于scanf/printf快于cin/cout

有些题目数据可能非常多,但是对数据的操作可能却相对简单,这时候不要因为I/O导致TLE,我想,几乎所有人都遇到过cin/cout换scanf/printf才能过的情形。

另外就是对于整型,可能手动解析是最快的(我还没试验,不能下定论)。因为scanf/sscanf/fscanf都是支持多种格式,所以实现上不可避免要各种判断。

最近做的一道题就是这样,用cin/cout超时,用scanf/printf大约0.7s,用自己手动解析的方式0.14s。

题目大致是先输入n(n<10^6),后面跟n行数据,每行都是整数。然后输出行数不定(和输入有关),也可以认为是和n同一个数量级。我最后的写法是:

// 这是手动解析int和输出int

#define  PARSE_INT( x ) do{ \
                x = 0; \
                int  sgn = 1; \
                while( *ptr < '0' || *ptr > '9' ) { if( *ptr == '-' ) sgn = -1; ++ptr; } \
                while( *ptr >= '0' && *ptr <= '9' ) { x = x*10 + *ptr - '0'; ++ptr; }  \
                x = x * sgn; \
        }while(0)

#define  OUTPUT( x ) do { \
                int  x0 = x; \
                char  num[32], *digit_pos = num + 31; \
                num[31] = '\n'; \
                while( x0 > 0 ) { \
                        *--digit_pos = x0 % 10 + '0'; \
                        x0 /= 10; \
                } \
                int  sz = num + 32 - digit_pos; \
                memcpy( optr, digit_pos, sz ); \
                optr += sz; \
        }while(0)

// 这是输入输出缓冲区,确保足够大
char  buff[MAXN*12];
char  obuff[MAXN*12];
……

// 一口气将所有输入全部读入缓冲区
n = fread( buff, 1, sizeof( buff ), stdin );
PARSE_INT( n );
while( n-- ) {
    PARSE_INT( x );
    .....
    OUTPUT( y );
}
// 一口气将所有输出
fwrite( obuff, 1, optr - obuff, stdout );


2> two pointers的实现

何谓two pointers?很多题目都是这个解题模式——先对数据排序,然后用两个指针遍历一遍数组,两个指针随着一定的限制条件向后或向前跳跃。

例如最近做的一道题,一个升序数组,当两个数之间的差恰好小于某个阈值,则进行一些计算,这时应该是两个指针依次向后走,他们之间的间距时大时小。

我的一个实现是:

for( int i = 0, j = 1; i < n; ++i ) {
    while( j < n && a[j] - a[i] <= threshold ) ++j;
    do_sth( i, j - 1 );
}
然后看到另一个人的实现是:

for( int i = 0, j = 1; j < n; ++j ) {
    while( a[j] - a[i] > threshold ) ++i;
    do_sth( i, j );
}
这两种实现对比一下,不难发现,我之前的实现是从前往后推,很有可能会推出界,所以要多个判断,而后面的实现是从后往前拉,有后面的指针作为天然的哨兵,不用担心越界。







你可能感兴趣的:([ACM] 经验总结(一))