0301_算法22级1班实验1

目录

Problem A 统计数字问题

1.题目描述

2.思路分析

3.代码实现

Problem B 字典序问题

1.题目描述

2.思路分析

3.代码实现

Problem C 最多约数问题

1.题目描述

2.思路分析

3.代码实现


 

Problem A 统计数字问题

1.题目描述

题目描述

问题描述:
         一本书的页码从自然数 1 开始顺序编码直到自然数 n。书的页码按照通常的习惯编排,每个页码都不含多余的前导数字 0。例如,第 6 页用数字 6 表示,而不是 06 或 006 等。数字计数问题要求对给定书的总页码 n,计算出书的全部页码中分别用到多少次数字 0,1,
2,…,9。
编程任务:
        给定表示书的总页码的 10 进制整数 n (1≤n≤109) 。编程计算书的全部页码中分别用到多少次数字 0,1,2,…,9。

输入

给出表示书的总页码的整数 n

输出

输出共有10行,在第 k 行输出页码中用到数字 k-1 的次数,k=1,2,…,10。

样例输入

11

样例输出

1
4
1
1
1
1
1
1
1
1

提示

输入范围1-1000000000

2.思路分析

        其实这个问题可以拆分为我们以前做过的规模更小的题目,总的来说,就是从1到输入的整形数据进行一个循环,在每一个循环周期内需要把当前页码数字的每一位拆出来,然后对每一位对应的数字进行累加计数。

        因此我们很容易写代码,循环直接用最简单的for循环,拆数字时可以用到%运算,计数也只需要开一个容量为10的整形数组,下标与0-9一一对应即可。

3.代码实现

#include 
 
using namespace std;
 
int num[10] = { 0 };
 
void cal(int n)
{
    while (n >= 9)
    {
        num[n % 10]++;
        n /= 10;
    }
    if (n != 0)
        num[n]++;
}
 
int main()
{
    int n;
    cin >> n;
    for (int i = n; i > 0; i--)
    {
        cal(i);
    }
    for (int i = 0; i <= 9; i++)
    {
        cout << num[i] << endl;
    }
    return 0;
}

 

Problem B 字典序问题

1.题目描述

题目描述

问题描述:
        在数据加密和数据压缩中常需要对特殊的字符串进行编码。给定的字母表 A 由 26 个小写英文字母组成 A={a,b,…,z}。该字母表产生的升序字符串是指字符串中字母按照从左到右出现的次序与字母在字母表中出现的次序相同,且每个字符最多出现 1 次。例如,a,b,ab,bc,xyz 等字符串都是升序字符串。现在对字母表 A 产生的所有长度不超过 6 的升序字符串按照字典序排列并编码如下。

1 2 26 27 28
a b z ab ac
编程任务:
        对于给定的长度不超过 6 的升序字符串,编程计算出它在上述字典中的编码。

输入

文件的第一行是一个正整数 k,表示接下来共有 k 行。
接下来的 k 行中,每行给出一个字符串。

输出

运行结束输出共有 k 行,每行对应于一个字符串的编码

样例输入

2
a
b

样例输出

1
2

2.思路分析

        整体问题的大框架我们很常见,就是首先输入问题个数n,然后输入n个待处理数据,依次进行运算,这里用一个while循环即可实现。这个题目最重要的问题是如何求解编码,我们首先来读题,题目要求字符串一定是无重复字母且升序的,因此有重复字母或者非升序的字符串是非法的,我们不做处理,输出0即可;而对于合法的字符串我们需要做以下操作(以bd为例):

        ①在bd之前,长度为1的字符串一定全部结束了,所以首先需要计算长度为1的字符串个数

        ②bd是b开头的两位字符串,因此在它之前a开头的两位字符串也结束了,再加上a开头的两位字符串个数

        ③在b开头的两位字符串中,bd之前还有bc,因此还需要加上b开头、bd之前的两位字符串个数

        ④此时bd之前的个数已经计算出来了,只需再加1就对应着bd的编码

        但是在写代码时发现,这个算法并不容易实现,对于多字符的字符串的适配性需要多次调试才完整得到;同时还涉及到a~z字母遍历的问题,我们用1~26一一对应即可。这里主要写了三个函数,f(i, j)用来计算i对应的字母为首字母、长度为j的所有字符串个数,g(j)用来计算长度为j的所有字符串个数,dic_order用来计算该字符串最终的编码。接下来我们对这三个函数一一进行分析。

        f(i, j)用来计算i对应的字母为首字母、长度为j的所有字符串个数。这里用了递归的思想,递归的出口是“首字母是i对应字母,长度为1的字符串个数”,显然只有一种情况,返回值为1;而长度大于1的时候就需要累加了,我们首字母不变、只考虑后面的j-1位,但我们要想递归仍然需要确定j-1位的首字母,很简单,要求无重复且升序,从【原来的首字母i加1】直到【26】都是满足条件的,因此我们需要用k从【原来的首字母i加1】到【26】循环,每一个可能的情况都是k对应的字母为首字母、长度为j-1的所有字符串个数,累加起来即可。这样,一个用到递归的f(i, j)就写好了。

        g(j)用来计算长度为j的所有字符串个数。这个函数在f(i, j)基础上特别容易,因为i从a~z循环,长度又是固定的,同样循环累加即可。

        dic_order用来计算该字符串最终的编码。根据我们之前的例子,首先计算长度小于所给字符串长度的所有字符串个数,我们设所给的字符串长度为sz,这里以be为例,写出需要做的步骤:

        ①计算长度为1的字符串个数,即g(1);

        ②再加上a开头的两位字符串个数,即f(1, 2);

        ③还需要加上b开头、be之前的两位字符串个数,在这一步需要注意,你已经不需要考虑b,只需要考虑e的位置,而第二位是从b+1也就是c开始的,所以需要加上c开头的一位字符串个数、d开头的一位字符串个数,这里又用到了循环,用t从s[0] - 'a' + 1 + 1(第一位b后面的字母)到s[sz-1] - 'a' (后一位e前面的字母)循环,每次需累加f(t, 1)

        这样,我们的编码就已经完全实现了,其他需要注意的小细节请参考下方的代码实现。这样写出来的代码是能够适配多个字符组成的字符串的,可以自行验证一下如abd等字符串的计算过程。

3.代码实现

#include
#include

using namespace std;

int f(int i, int j)
{
    if (j == 1)
        return 1;
    int sum = 0, k;
    for (k = i + 1; k < 27; ++k)
    {
        sum += f(k, j - 1);
    }
    return sum;
}
int g(int j)
{
    int sum = 0, i;
    for (i = 1; i < 27; ++i)
    {
        sum += f(i, j);
    }
    return sum;
}
int dic_order(string s)
{
    int i, j, sz = (int)s.size(), sum = 0, t = 0, s0;
    for (i = 1; i < sz; ++i)
    {
        sum += g(i);
    }
    s0 = s[0] - 'a' + 1;
    for (i = 1; i < s0; ++i)
    {
        sum += f(i, sz);
    }
    for (i = 1, t = s0; i < sz; ++i)
    {
        for (j = t + 1; j < s[i] - 'a' + 1; ++j)
        {
            sum += f(j, sz - i);
        }
        t = s[i] - 'a' + 1;
    }
    return sum + 1;
}
int main()
{
    int i, n;
    string s;
    cin >> n;
    while (n--)
    {
        cin >> s;
        for (i = 0; i < (int)s.size() - 1; ++i)
        {
            if (s[i] >= s[i + 1])
            {
                break;
            }
        }
        if (i < (int)s.size() - 1)
        {
            cout << 0 << endl;
        }
        else
        {
            cout << dic_order(s) << endl;
        }
    }
    cout << endl;
}

 

Problem C 最多约数问题

1.题目描述

题目描述

问题描述:
        正整数 x 的约数是能整除 x 的正整数。正整数 x 的约数个数记为 div(x)。例如,1,2,5,10 都是正整数10 的约数,且 div(10)=4。设 a 和 b 是 2 个正整数,a≤b,找出 a 和 b之间约数个数最多的数 x。
编程任务:
        对于给定的 2 个正整数 a≤b,编程计算 a 和 b 之间约数个数最多的数。

输入

第1 行有 2 个正整数 a 和 b。

输出

若找到的 a 和 b 之间约数个数最多的数是 x,将 div(x)输出

样例输入

1 36

样例输出

9

提示

输入范围:1-1000000000

2.思路分析

        其实这个问题我们不陌生,无非就是求约数个数,针对这个题我们只需要在此基础上不断更新最大的约数个数和对应的数即可。更新比较简单,这里不再赘述,计算约数个数的函数这里重新整理一下思路:因为一个数的约数是成对出现的,所以用i从1开始自加,直到原数的平方根遍历,如果原数除以i恰好还等于i,说明此时i是平方根,虽成对但二者相等算作一个;其余能整除的情况每次加2即可。

3.代码实现

#include 
#include 
using namespace std;

int countDivisors(int n)
{
    int cnt = 0;
    for (int i = 1; i <= sqrt(n); i++)
    {
        if (n % i == 0)
        {
            if (n / i == i)
                cnt++;
            else
                cnt += 2;
        }
    }
    return cnt;
}

int main()
{
    int a, b;
    cin >> a >> b;

    int maxDivisors = 0;
    int numWithMaxDivisors = 0;

    for (int i = a; i <= b; i++)
    {
        int divisors = countDivisors(i);
        if (divisors > maxDivisors)
        {
            maxDivisors = divisors;
            numWithMaxDivisors = i;
        }
    }

    cout << maxDivisors << endl;

    return 0;
}

 

 

你可能感兴趣的:(0301_算法22级1班实验1)