刘汝佳《算法竞赛入门经典(第二版)》习题(三)

刘汝佳《算法竞赛入门经典(第二版)》第三章习题(一)

习题3-1 得分(ACM/ICPC Seoul 2005UVa1585

给出一个由OX组成的串(长度为1~80),统计得分。每个O的得分为目前连续出现的O的个数,X的得分为0。例如:OOXXOXXOOO的得分为1+2+0+0+1+0+0+1+2+3

解析

因为X的得分为0,所以只需要统计O的得分,另外需要注意的是每次遇到X,统计O得分的变量都要置0

#include 
#include 

using namespace std;

int main (void)
{
    string s;
    cin >> s;
    int onum = 0,sum = 0;
    for (int i = 0; i < s.size(); i++)
    {
        if (s[i] == 'O')
        {
            onum++;
            sum += onum;
        }
        else if (s[i] == 'X')
            onum = 0;
    }
    cout << sum << endl;
    return 0;
}

习题3-2 分子量(ACM/ICPC Seoul 2007UVa1586

给出一种物质的分子式(不带括号),求分子量。本题中的分子式只包含 4 种原子,分别为 C H O N ,原子量分别为 12.01 1.008 16.00 14.01 (单位: g/mol )。例如: C6H5OH 的分子量为 94.108g/mol

解析

这道题在ACM/ICPC Seoul 2007中需要输入的是n1≤n≤99)个分子量(管他呢),而且并没有给出数据范围和运行时间限制,所以暂且当做标准限制来做吧(下面的题目除非书中注明,否则同样看待)。

因为分子式中数字对应的一定是其前面的分子,所以只需要判断分子是哪个,以及分子后面的分子数,再根据对应分子的分子量计算就好了。然而麻烦就麻烦在分子数并不一定是个位数,所以我们要怎么将代表分子数的字符串转换成数字呢?c标准库提供了atoiatofatolatoll函数(需包含头文件stdlib.hcstdlib)将字符串分别转换成intdoublelonglong long型,我们将两个分子之间的数字存放在一个字符型数组,再调用atof函数转换即可。

说起来挺简单的,但实现起来需要考虑的东西还蛮多。不废话,上代码:

#include 
#include 
#include 
#include 

using namespace std;

const char name[] = "CHON";
const double fweight[] = {12.01,1.008,16.00,14.01};

int main (void)
{
    char c[5];
    string s;
    cin >> s;
    double sum = 0.0, weight = 0.0;
    int num = 0;
    for (int i = 0; i < s.size(); i++)//遍历字符串,找出分子
    {
        if (isalpha(s[i]))
        {
            for (int j = 0; j < 4; j++)//将分子对应的分子量赋给weight
                if (s[i] == name[j])
                {
                    weight = fweight[j];
                    break;
                }
            num = 0;//将用来移位的变量清零
            if (isalpha(s[i+1]) || i+1 == s.size())//分子式后面若没有分子数则其分子数默认为1
                sum += weight;
            else
            {
                for (int k = i+1; k < s.size() && isdigit(s[k]); k++)//将表示分子数的字符串转换为double型数据
                    c[num++] = s[k];
                sum += atof(c)*weight;//计算分子量
            }
            memset(c, '\0', sizeof(c));//将用于存放分子数的数组清空
        }
        else
            continue;
    }
    cout << sum << "g/mol" << endl;
    return 0;
}

习题3-3 数数字(ACM/ICPC Danang 2007UVa1225

把前nn≤10000)个整数顺次写在一起:123456789101112…数一数0~9各出现多少次(输出10个整数,分别是019出现的次数)。

解析

刚看到这道题的时候我一直以为这些数字的出现是有规律的,想了很久,然而……失败了(等我用暴搜写完后在网上找各路大神的解法才悲剧地发现:根本没有规律T.T)。数据范围不大,原题的时间限制为3000ms,所以直接暴搜吧。有两种思路,第一种是对1~n的整数分别进行数字统计,得出结果;第二种是将1~n的整数一个个转成字符串,然后统计,得出结果。

对于第一种思路,一般做法是直接一个1~n的大循环,对每个整数一步步拆分,统计数字出现次数,我就是这么写的。另外我在网上看到一种比较特别的做法:直接用10000×10的二维数组做成一个查询表,再根据输入的值查询输出,下面1~19的查询表,帮助理解:


刘汝佳《算法竞赛入门经典(第二版)》习题(三)_第1张图片

解法一

#include 
#include 
#include 

int main (void)
{
    int a[10];
    int n,num;
    scanf ("%d",&n);
    memset(a, 0, sizeof(a));
    for (int i = 1; i <= n; i++)
    {
        int temp = i;
        while (temp)//拆分整数
        {
            num = temp%10;
            temp /= 10;
            a[num]++;
        }
    }
    for (int i = 0; i < 10; i++)
        (i == 0)?printf ("%d",a[i]):printf (" %d",a[i]);
    return 0;
}

解法二
#include 
#include 
#include 

int a[10000][10];

int main (void)
{
    memset (a, 0, sizeof(a));
    for (int i = 1; i < 10000; i++)
    {
        for (int j = 0; j < 10; j++)
            a[i][j] = a[i-1][j];
        for (int k = i; k; k /= 10)
            a[i][k%10]++;
    }
    int n;
    scanf ("%d",&n);
        for (int j = 0; j < 9; j++)
            (j)?printf (" %d",a[n][j]): printf ("%d",a[n][j]);
        printf ("\n");
    return 0;
}

在本题中解法二相对于解法一在速度上并没有什么优势,但这个制表查询的想法还是值得学习的(反正我想不到- -)。

对于第二种思路,用sprintf函数(虽然itoa也可以,但与ANSI标准不兼容,不建议使用)将每个整数转成字符串是关键,直接看代码就好了。

解法三
#include 
#include 
#include 
#include 

int main (void)
{
    int num[10];
    int n;
    scanf ("%d",&n);
    char c[10];
    memset(num, 0, sizeof(num));//初始化存放统计数字的数组
    for (int i = 1; i <= n; i++)//对1~n的数字逐一统计
    {
        memset(c, '\0', sizeof(c));//初始化存放转换结果的字符数组
        sprintf(c, "%d", i);//将数字转换为字符存到数组c中
        for (int j = 0; j < 10; j++)//对字符数组c进行扫描,对数字进行统计
            if (isdigit(c[j]))
            {
                switch (c[j])
                {
                        case'0':
                            num[0]++;
                            break;
                        case'1':
                            num[1]++;
                            break;
                        case'2':
                            num[2]++;
                            break;
                        case'3':
                            num[3]++;
                            break;
                        case'4':
                            num[4]++;
                            break;
                        case'5':
                            num[5]++;
                            break;
                        case'6':
                            num[6]++;
                            break;
                        case'7':
                            num[7]++;
                            break;
                        case'8':
                            num[8]++;
                            break;
                        case'9':
                            num[9]++;
                            break;
                }
            }
    }
    for (int i = 0; i < 10; i++)
        (i == 0)?printf ("%d",num[i]):printf (" %d",num[i]);
    return 0;
}

习题3-4 周期串(UVa 455

如果一个字符串可以由某个长度为k的字符串重复多次得到,则称该串以k为周期。例如,abcabcabcabc3为周期(注意,它也以612为周期)。

输入一个长度不超过80的字符串,输出其最小周期。

解析

题目已经说得很清楚,做法也很简单,只需要从1开始假设周期,然后扫描字符串的一半,验证是否是这个周期,是则输出周期,否则假设的周期增1

#include 
#include 
#include 

int main (void)
{
    string s;
    int kase = 0,T = 0;//kase判断是否存在周期
    cin >> s;
    for (int i = 0; i < s.size()/2; i++)//扫描字符串的一半
    {
        T++;
        if (s.size()%T == 0)
        {
            kase = 1;
            for (int j = 0; j < T; j++)//验证假设的第一个周期是否成立
            {
                for (int k = 0; k+T < s.size(); k += T)
                    if (s[j] != s[k+j])
                    {
                        kase = 0;
                        break;
                    }
                if (!kase)//一旦发现周期不成立即退出,减少不必要的时间消耗
                    break;
            }
        }
        if (kase)//同上一个kase作用相同
            break;
    }
    if (kase)
        printf ("%d\n",T);
    return 0;
}









你可能感兴趣的:(题目)