UVA 213 ACM-ICPC World Finals 1991 信息解码

时间:2021-02-23 18:21:34

UVA 213 ACM/ICPC World Finals 1991 信息解码

标签(空格分隔): 算法竞赛 编程错题 紫书


原题很长,在这里我就不做过多叙述了,我直接贴个链接吧。
UVA 213 ACM/ICPC World Finals 1991 信息解码
题目里面包含了连个很有用的自定义的函数实现:连接被换行隔开的字符串readchar、二进制转十进制函数readint。

这个题看答案简单的很,就是三个函数模块,但是思考起来和实现起来对现在的我来说实在太难,而且书上的解释太少了,我也看不懂他到底是如何实现的。在这里我详细解释一下这道题的解法和我犯的错误。
先贴代码和注释。

#include <stdio.h>
#include <string.h>

int code[8][1<<8];      //设置编码头数组,首下标代表示例序列中每个元素的数字个数,尾下标代表在这一列中的字符序号,用位运算表示
int readchar()          //返回读取到的第一个不是\n(换行符)和\r(回车符)的字符
{
    for (;;)
    {
        int ch=getchar();
        if (ch!='\n'&&ch!='\r') return ch;
    }
}

int readint(int c)      //将二进制C转换为十进制数 
{
    int v=0;
    while (c--)
        v=v*2+readchar()-'0';
    return v;
}

int readcodes()         //按题目中示例序列的规律,生成编码数组
{
    memset(code,0,sizeof(code));
    code[1][0]=readchar();
    for (int len=2;len<=7;len++)
    {
        for (int i=0;i<(1<<len)-1;i++)
        {
            int ch=getchar();
            if (ch==EOF)    return 0;
            if (ch=='\n'||ch=='\r') return 1;
            code[len][i]=ch;
        }
    }
    return 1;
}

int main()
{
    while (readcodes())
    {
        for (;;)
        {
            int len=readint(3);
            if (len==0) break;
            for (;;)
            {
                int v=readint(len);
                if (v==(1<<len)-1)  break;
                putchar(code[len][v]);
            }
        } 
        putchar('\n');
    }
    return 0;
}

说来惭愧,这道鬼题我看了三天。虽然大多数时间用在美赛上,但是花在这道题上的时间还是让人无法忍受,直到我看到这是世界总决赛题,当然,我觉得这是里面最水的一道吧。
下面开始说各个问题。

    readchar():由于题目提到,换行符分开的二进制字符串,也要连接起来,所以单独写了这个输入函数。为什么用了个循环呢?主要还不是为了防止多个回车换行,而是为了让接下来识别的小块字符串连起来。比如说,如果没有for,那么这个程序如果碰到"01\n1",而每次要识别3个字符,那就会将01\n读入,这时候二进制转十进制函数readint就会发生异常;如果有for,回车换行就会被全部跳过。
    readint():将输入的二进制数转变为十进制数。这个算法一定要学会。
    readcodes():生成一个二维数组。观察示例序列可以看出,数字位数依次增加,位数相同的二进制数按升序排列,最大值为2^n-2。根据这个规律,可以将字符串拆开,先按位数的规律,再按升序规律检索。例如,输入"$#**\",则会被排列为:

    code[0]
    code[1]     $
    code[2]     #   *   *
    code[3]     \

    然后就可以按位数和序号进行检索了。

    之后是主函数。先用while循环来输入编码头,再进入第一个for循环,意为对第一个字符串整体进行处理;在进入第二个for循环,意为对该编码长度下的这部分字符串进行处理。

在我做这道题的时候,碰到了两个大错误:1.readchar没加for循环。但是后来我单步一下,发现了不加for循环的错误结果,所以就加了,对了。2.程序line41~line55,算法对,但是刚开始我把len定义在line42,本地运行没问题,但提交就超时。直到我把len定义到了函数体内,就不超时了。这说明:尽可能减小变量定义范围,能减少耗时。

这道题可以说是一道标准的难题,代码很少,但是思考不易,要实现它所需要的四个分函数,正确实现也不容易。