蓝桥杯书的笔记(二:接上篇蓝桥云课里的内容,C++)

本文通过蓝桥杯竞赛真题,详细解析了打表法和模拟法在算式问题、既约分数、天干地支纪年、斐波那契数列等场景中的实际操作,展示了如何结合递推与递归算法解决这些问题,强调了在竞赛中的高效解题策略和思维训练的重要性。
摘要由CSDN通过智能技术生成

https://www.lanqiao.cn/courses/3993/learning/?id=248899

打表法和模拟法

模拟法是比赛中最常用的方法,使用各种算法大都离不开模拟,而对于一些只是需要结果的题目来说打表法是一个非常好的解决方案,而且对于数论等其他需要找规律的题目来说,打表法是一个非常有用的方法。

模拟法和打表法,经常会同时出现,因为打表就要按照题目的意思去模拟。今天我们就从蓝桥杯的真题出发,给大家讲解一下,打表法和模拟法的应用。

算式问题

题意:

题目描述:

本题为填空题,只需要算出结果后,
在代码中使用输出语句将所填结果输出即可。!!!!!!!!!!!!

看这个算式:

☆☆☆ + ☆☆☆ = ☆☆☆

如果每个五角星代表 1 ~ 9 的不同的数字。这个算式有多少种可能的正确填写方法?

173 + 286 = 459
295 + 173 = 468
173 + 295 = 468
183 + 492 = 675

以上都是正确的填写法!

注意:111+222=333 是错误的填写法!因为每个数字必须是不同的!也就是说:1 ~ 9 中的所有数字,每个必须出现且仅出现一次!

注意:不包括数字 “0”。

注意:满足加法交换率的式子算两种不同的答案。 所以答案肯定是个偶数!

运行限制:

 1. 最大运行时间:1s
 2. 最大运行内存: 128M

首先我们能了解到这道题只需要答案,那么对于时间要求就等于无限,那我们可以使用模拟方法,因为只需要输出答案即可,只要能够在比赛的时长里跑出来即可。

注意既然我们不需要考虑时间问题和代码问题,我们一定要将代码设计的具有较高的逻辑性和准确性。

这个题的正解是搜索算法,但是既然只要答案我们求快、求简单,在这里我们使用另一种方式进行解答。

这里有三个数字 我们称 A + B = C 且各个位上的数字不同。
我们这里借助桶排序的思想来判断 1-9 这些数字有没有占用。
所以我们定义一个判断函数,用于判断 A B C 三个数字是否符合要求。

然后暴力枚举:

A 从 123 到 987 开始枚举
有很多同学开始抬杠 111-999 岂不是更简单,因为 123
是最小的符合要求的数字,可以减少枚举的次数,987 是最大的符合要求的数字。

B 从 123 到 987-A 枚举
这时候又会有很多同学来问,为什么不直接枚举与 A 不一样的数字呢,那么又得考虑每一位的问题,这样的模拟已经不是暴力法了,我们要做的就是在不改变完成难度的情况下,减少复杂度。所以要分清注次。

C = A + B 这时候只要检查 A B C 是否符合要求即可。

完整代码:

#include <iostream>
#include <map>
using namespace std;

int flag[11];

int check(int a,int b,int c)
{

    for(int i=0;i<10;i++) flag[i]=0;
    flag[0]=1;
    while(a!=0)
    {
        if(flag[a%10]==1) return 0;
        else flag[a%10]=1;

        if(flag[b%10]==1) return 0;
        else flag[b%10]=1  ;


        if(flag[c%10]==1) return 0;
        else flag[c%10]=1  ;

        a=a/10;
        b=b/10;
        c=c/10;

    }
    return 1;
}

int main()
{
    int ans=0;

    for(int a=123;a<=987;a++)
        for(int b=123;b<=987-a;b++)
        {
            int c=a+b;
            if(check(a,b,c))
            {

                ans++;
                cout<<a<<" + "<<b<<" = "<<c<<endl;
            }

        }
    cout<<ans<<endl;
}

注意
题目要求是只输出答案,我们讲模拟的代码提交是一分不得的,所以按照题目要求,以下才是正确答案。

#include <iostream>
using namespace std;
int main()
{
 
  cout<<336<<endl;
  return 0;
}

求值

题目描述:

本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。!!!!

题目分析
这道题乍一看,是一道数论题目,实际上他确实一道数论题目,但是由于是道填空题,所以我们采用模拟法打表做。

题目中的描述是找约数,那我们定义个找约束个数的函数,然后枚举即可。

这样不考虑时间复杂度,我们采取暴力法,尽快完成题目,让程序去跑答案,节省下时间来去做其他的题目。

学习了约数后,小明对于约数很好奇,他发现,给定一个正整数 t,总是可以找到含有 t 个约数的整数。小明对于含有 t 个约数的最小数非常感兴趣,并把它定义为 St​。

例如 S1=1,S2=2,S3=4,S4=6,⋅⋅⋅

现在小明想知道,当 t=100 时,S1​00 是多少?

运行限制:

 1. 最大运行时间:1s
 2. 最大运行内存:128M

暴力写约束计数函数。

#include <iostream>
#include <map>
using namespace std;

int cnt(int a){
    int ans = 0;
    for (int j = 1; j <= a; j++)
        if (a % j == 0)
            ans++;
    return ans;
}

int main()
{
    for(int i=1;1;i++)
    {

        cout<<cnt(i)<<" "<<i<<endl;
        if(cnt(i)==100) break;
    }

}

要提交的代码:

#include <iostream>
using namespace std;
int main()
{
 
  cout<<45360<<endl;
  return 0;
}

既约分数

题目描述:

本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。

如果一个分数的分子和分母的最大公约数是 1,这个分数称为既约分数。

例如 $\frac{3}{4} ,\frac{1}{8} ,\frac{7}{1}$, 都是既约分数。

```txt

请问,有多少个既约分数,分子和分母都是 12020 之间的整数(包括 12020)?
运行限制

 1. 最大运行时间:1s
 2.最大运行内存:128M

我们看到这种题,现在一眼就知道只是到纯暴力的题目,即暴力枚举然后依据题目要求模拟即可。

但是这种简单题在比赛中是来送分的,我们要花很少的时间做完,才有时间做其他的题目,这就要求我们对这种题目的熟练度极高,要做到,看到题目,想到思路手里能直接写出来才可以。

这里有一个巧妙的方法是因为分子与分母是对称的我们可以少枚举一半,不过有些同学可能没想明白,没关系,我们用普通的办法,只要能够快速的编程并找到答案,思路正确性能够保证的话,其他的都是可有可无的。

这题目我们首先要两个数是否互质,即最小公约数为 1,我们就定义一个 GCD() 求最小公约数的算法,这里我们采用的是递归的方法。

最方便的写法:

int GCD(int a,int b)
{
    return a%b?GCD(b,a%b):b;
}

也可以这样写(同义的替换)

int GCD(int a,int b)
{
    if(a%b==0) return b;
    //如果a%b==0 代表可以被整除,那么b就是最大公约数

    else return GCD(b,a%b)

    // 如果不能被整除,那么就先取余数,代码会保证左侧是大的,右侧是小的数字,所以使用时不必进行大小检查,即使a<b也会再一次递归后变成b,a在进行计算。

    //这样就能按照辗转相除法求解。(小学五年级的课程吧)
}

也可按照算法的字面意思写:

int gcd(int a,int b)
{
    int temp;
    while(b)
    {    
        /*利用辗除法,直到b为0为止*/
        temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}

然后这个题目我们就可以进行枚举了。

外层循环为 a,假设是分母,内层循环是 b 这样就可以进行枚举
a 和 b 都是 1 到 2020 那这个题,就非常简单了

完整代码:

#include <iostream>
using namespace std;

int gcd(int a,int b)
{
    int temp;
    while(b)
    {
        /*利用辗除法,直到b为0为止*/
        temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}

int main()
{

    int ans=0;
    for(int a=1;a<=2020;a++)
    {
        for( int b=1;b<=2020;b++)
        {
            if(gcd(a,b)==1) ans++;
        }

    }
    cout<<ans<<endl;

}

提交代码:

#include <iostream>
using namespace std;
int main()
{
 
  cout<<2481215<<endl;
  return 0;
}

天干地支

题意:

题目描述:

古代中国使用天干地支来记录当前的年份。

天干一共有十个,分别为:甲(jiǎ)、乙(yǐ)、丙(bǐng)、丁(dīng)、戊(wù)、己(jǐ)、庚(gēng)、辛(xīn)、壬(rén)、癸(guǐ)。

地支一共有十二个,分别为:子(zǐ)、丑(chǒu)、寅(yín)、卯(mǎo)、辰(chén)、巳(sì)、午(wǔ)、未(wèi)、申(shēn)、酉(yǒu)、戌(xū)、 亥(hài)。

将天干和地支连起来,就组成了一个天干地支的年份,例如:甲子。

2020 年是庚子年。

每过一年,天干和地支都会移动到下一个。例如 2021 年是辛丑年。

每过 60 年,天干会循环 6 轮,地支会循环 5 轮,所以天干地支纪年每 60 年轮回一次。例如 1900 年,1960 年,2020 年都是庚子年。

给定一个公元纪年的年份,请输出这一年的天干地支年份。

输入描述:

输入一行包含一个正整数,表示公元年份。

其中有 ,输入的公元年份为不超过 9999 的正整数。

输出描述:

输入一行包含一个正整数,表示公元年份。

输入输出样例:

输入

2020


输出

gengzi

运行限制

 1. 最大运行时间:1s
 2. 最大运行内存: 128M

这个题目是模拟法中最讨厌也最常见的一种,可能还有比这更复杂的,但这道题,已经初具代表性。

他的种类比较多,天干就有 10 种 ,地支有 12 种

现在我们知道了 2020 年是庚子年,我们这里既可以是除留余数来判断 N 年是什么天干和什么地支,我们也可以直接暴力使用循环做,这样的话 9999 的复杂度也跑不了多久。实现起来很简单,我们讲这个比较难的。

我们先判断 0000 年的天干 和地支 。

  • 根据题意 0000 年 距 2020 年 早了 2020 年 。
  • 已知天干 有 10 个, 那么 2020%10=0 剩下的都是整个轮回,即到了 0000 年 是庚 X 年,即天干是 庚 。

再按照这个方法算地支 是 2020%12=4 及还要向前推四年 地支为申。

即 0001 年为庚申年,那么根据模拟法可知。

N-1%10=0 时 天干为庚

N-1%10=1 时 天干为辛

以此类推

N-1%12=0 时 地支为申

N-1%12=1 时 地支为酉 …

一种优雅的实现方式

#include <iostream>
using namespace std;

string tg[10] = {"geng", "xin", "ren", "gui", "jia", "yi" , "bing", "ding", "wu", "ji"};

string dz[12] = {"shen", "you", "xu", "hai", "zi", "chou", "yin", "mou", "chen", "si", "wu", "wei"};
int main()
{
    int year;
    cin >> year;
    cout<<tg[year%10]<<dz[year%12]<<endl;
}

总结

对于这种简单的模拟题,不需要借助算法,只要暴力的题目,我们都可以打表模拟,然后提交答案,在比赛时有的是输出答案,填空,比赛时注意分辨。

这章难度较低,但是对于熟练度要求较高。

多做简单的思维题,进行训练才能为后期的算法学习打下良好的基础,无论你学了多厉害的算法,如果思维训练不够,到了考场也是两眼一黑,手足无措。

而那些思维很好的同学,即使某一道题的算法我不会,但是我会有新的想法能接触这道题,我们现在所接触的所有算法,不都是某一个大牛,在不经意间发现,经过各种优化到我们手里的吗。

算法是工具,思维才是最重要的,我们这门课程不仅讲算法,还希望能够让各位提高思维能力。

递推法与递归法

在计算机中,递推法是用于数值求解的一个重要算法。

递推算法的特点
一个问题的求解需要大量重复计算,在已知的条件和所求问题之间总存在着某种相互联系的关系,在计算时,我们需要找到这种关系,进行计算(递推关系式)。

即递推法的关键,就是找到递推关系式,这种处理方式能够将复杂的计算过程,转化为若干步骤的简单重复运送,充分利用计算机运行程序时的时间局部性和空间局部性。

递推算法的思想:
首要问题是先找到各个相邻数据项之间的递推关系;
递推关系避开了求通项公式的麻烦,且有些题目的通项公式很难求,或者不能进行求解;
将复杂问题分解为若干步骤的简单运算;
一般来说递推算法就是一种特殊的迭代算法。

递推算法解题的基本思路:
将复杂计算转换为简单重复运算;
通过找到递推关系式进行简化运算;
利用计算机的特性,减少运行时间。

递推算法的一般步骤:

  1. 根据题目确定数据项,并找到符合要求的递推关系式;
  2. 根据递推关系式设计递推程序;
  3. 根据题目找到递推的终点;
  4. 单次查询可以不进行存储,多次查询都要进行存储;
  5. 按要求输出答案即可。

递归算法设计的一般步骤:

  1. 根据题目设计递归函数中的运算部分;
  2. 根据题目找到递归公式,题目可能会隐含给出,也可能需要自己进行推导;
  3. 找到递归出口,即递归的终止条件。

斐波纳契数列 fibonacci 问题

在一定情况下,同一个问题可以使用用递归也可以使用递推解答。一般一个问题的递推关系和递归关系都好求的话就都可以解题。

当然如果题目只有一个关系好求,那就最好采用关系好求的办法。

题意及输入输出样例:

斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”。

指的是这样一个数列:0112358132134、……

在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=0F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)

请求出该数列中第n个数字(n从1开始计数)是多少。


输入样例

样例1输入
6

样例2输入
4

输出样例

样例1输出
8

样例2输出
3

运行限制:
1. 最大运行时间:1s
2. 最大运行内存:128M

对于上面的式子我们进行了如下运算(也可以学一学这样的表示法)

[0]=0

[1]=1

[2]=0+1

[3]=1+1=2

[4]=1+2=3

[5]=2+3=5

[6]=5+3=8

题目解析:

  • 这个题给出递推式 F(n) = F(n-1) + F(n-2)
  • 转化为可用的递推关系,即 F(n) + F(n+1) = F(n+2)

这一通过从 n=1 开始循环即可完成递推,当然也可以使用递归法。

我们可以知道 F(n)=0 n=0 ,F(n)=1 n=1 这就是递归出口,能让递归停止的条件。

这道题不是多次询问问题,不需要存储直接计算的复杂度是最低的。

递归算法的通用框架如下:

do(a,b,c...)
{
    //递归终止条件,即出口
    if(a==? ,b==? ,....) return 

    //递归条件
    if(条件1)
        do(参数1)

    else(条件2)
        do(参数2)

}

如本题,各子式间存在计算关系,可以化为:

do(a)
{
    if(a==0) return 0;
    if(a==1) return 1;

    return do(a-1)+do(a-2);
}

递推算法代码:

#include <iostream>
using namespace std;

int main()
{

    int n; //第几个数
    int x=0; //F(n)
    int y=1; //F(n+1)
    int ans; //F(n+2)

    cin>>n;

    if(n==0) ans=0;
    else if(n==1) ans=1;
    else {
        for(int i=2;i<=n;i++)
        {
            ans=x+y;
            x=y;
            y=ans;
        }
    }
    cout<<ans<<endl;

}

递归算法代码:

 
#include <iostream>
using namespace std;

int fn(int n)
{
    //递归出口1
    if(n==0)
        return 0;

    //递归出口2
    else if(n==1 )
        return 1;

    else
        return fn(n-1)+fn(n-2); //递归关系式
}


int main()
{

    int n; //第几个数
    int ans;

    cin>>n;

    ans=fn(n);

    cout<<ans<<endl;

}

存储型的递推与递归

我们在开始就讲过题目是分存储和非存储的,上面那个题目就是此询问,
如果改为多次询问我们该怎么办,我们会采用存储的方式,存储的方式适用于大部分的的多次查询问题

修改后的题目:

斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”。

指的是这样一个数列:0112358132134、……

在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=0F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)。

我们将进行M次查询,每次输入一个N,其中n小于30。

请求出该数列中第n个数字(n从1开始计数)是多少?

样例:

输入样例

样例1输入:

6
4
2
7
8
8
10

样例2输入:

8
13
23
14
17
24
16
10
11

输出样例

样例1输出:

3
1
13
21
21
55

样例2输出:

233
28657
377
1597
46368
987
55
89

运行限制:
1. 最大运行时间:1s
2. 最大运行内存:128M

这道题跟上面一道题的算法原理相同,只是增加了多次查询的复杂度,所以仅需修改这一点即可。

只要不是交互题,评测机的输入与输出是分开的,只有你的输出会用来跟答案比较,所以我们只用关心我们的输出即可。

递推算法代码:

#include <iostream>
using namespace std;
int F[35];

void init()
{
    F[0]=0;

    F[1]=1;

    for(int i=2;i<=30;i++)
    {
        F[i]=F[i-1]+F[i-2];
    }
}
int main()
{

    int m; //m次查询
    int n; //第几个数
    init();

    cin>>m;

    while(m>0){
        m-=1;
        cin>>n;
        cout<<F[n]<<endl;
    }
}

存储答案的递推法,才是最常使用的递推法。

递归算法代码:

#include <iostream>
using namespace std;
int F[35];

int fn(int n)
{
    //递归出口1
    if(n==0)
    {
        F[0]=0;
        return 0;
    }

    //递归出口2
    else if(n==1 )
    {
        F[1]=1;
        return 1;
    }

    else
    {
        F[n]=fn(n-1)+fn(n-2);
        return F[n]; //递归关系式
    }
}

int main()
{
    int m; //m次查询
    int n; //第几个数

    fn(30);
    cin>>m;

    while(m>0){
        m-=1;
        cin>>n;
        cout<<F[n]<<endl;
    }
}

数字三角形问题

在这里插入图片描述
样例:

输入:

5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5


输出:
30

1. 最大运行时间:1s
2. 最大运行内存:128M

解决该题目的方式有很多,包括动态规划, 枚举都可以解决这个问题。

我们从递推的思想出发,假设我们从顶层沿着某条路径已经走到了第 i 层,正向着 i+1 层前进, 两条可行路径中我们肯定会选择最大的方向前进,为此我们可以采用递推中的反向递推,即逆推的方式解决,设 a[i][j] 存放从 i,j 出发到达第 n 层的最大值。

我们可以写出递推式:

a[i][j] = max{a[i][j]+a[i+1][j],a[i][j]+a[i+1][j+1]}

则逆推到出发点 a[1][1] 为题目所求答案,即第一层到第 N 层的最大值。

代码:

#include<iostream>
using namespace std;

int main()
{
    int n; //n层
    int a[101][101]; //路径矩阵
    cin>>n;

    //输入数字三角形的值
    for (int i=1; i<=n; i++)
    {
        for (int j=1; j<=i; j++)
        {

        cin>>a[i][j]; //输入原始数据

        }
    }

    //递推开始

    for (int i=n-1; i>=1; i--)//从最后一层逆推
    { 
        for (int j=1; j<=i; j++)
        {

            if (a[i+1][j]>=a[i+1][j+1])
                a[i][j]+=a[i+1][j];     //路径选择

            else  
                a[i][j]+=a[i+1][j+1];
        }
    }

    cout<<a[1][1]<<endl;
}

枚举法

枚举算法解题的基本思路:

  1. 确定枚举解的范围,以及判断条件
  2. 选取合适枚举方法,进行逐一枚举,此时应注意能否覆盖所有的可能的解
  3. 在枚举时使用判断条件检验,留下所有符合要求的解。

枚举算法的一般步骤:

  1. 根据题目确定枚举的范围,并选取合适的枚举方式,不能遗漏任何一个真正解,同时避免重复。
  2. 为了提高解决问题的效率,看题目是否存在优化,将可能成为解的答案范围尽可能的缩小。
  3. 根据问题找到合理并、准确好描述并且好编码的验证条件。
  4. 枚举并判断是否符合第三步确定的的条件,并保存符合条件的解。
  5. 按要求输出枚举过程中留下的符合条件的解。

简单型枚举

简单型枚举就是可以通过简单的 for 循环嵌套就可以解决的问题。

42 点问题

题意:

众所周知在扑克牌中,有一个老掉牙的游戏叫做24点,选取4张牌进行加减乘除,看是否能得出24这个答案。

现在小蓝同学发明了一个新游戏,他从扑克牌中依次抽出6张牌,注意不是一次抽出,进行计算,看是否能够组成 42 点,满足输出YES,反之输出 NO。

最先抽出来的牌作为第一个操作数,抽出牌做第二个操作数,运算结果在当作第一个操作数,继续进行操作。

除不尽的情况保留整数。

请设计一个程序对该问题进行解答。

在这里插入图片描述
我们创建 5 个 Vector ,分别用来存放 1-5 次的运算结果,非常简单。我们答案就采用这种方式。

代码:

#include <iostream>
#include <vector>
using namespace std;

int a[10];
vector <int> ans[10];

int main()
{
    for(int i=0; i<6; i++)
    {
        char c;
        cin>>c;
        if(c=='A')
            a[i]=1;

        else if(c=='J')
            a[i]=11;

        else if(c=='Q')
            a[i]=12;

        else if(c=='K')
            a[i]=13;

        else
            a[i]=(c-'0');
        //cout<<a[i]<<endl;
    }

    ans[0].push_back(a[0]);

    for(int i=1; i<=5; i++)
    {
        for(int j=0; j<ans[i-1].size(); j++)
        {
            ans[i].push_back(ans[i-1][j]+a[i]);
            ans[i].push_back(ans[i-1][j]-a[i]);
            ans[i].push_back(ans[i-1][j]*a[i]);
            ans[i].push_back(ans[i-1][j]/a[i]);
        }
    }

    //cout<<ans[5].size()<<endl;
    int flag=0;

    for(int i=0; i<ans[5].size(); i++)
    {
        if(ans[5][i]==42)
        {
            flag=1;
            break;
        }
    }

    if(flag==1)
        cout<<"YES"<<endl;

    else
        cout<<"NO"<<endl;
}

组合型枚举

就是排列组合问题。
在这里插入图片描述

公平抽签(题目是字母的话)

题意:

小A的学校,蓝桥杯的参赛名额非常有限,只有m个名额,但是共有n个人报名,其中m<=n。作为老师非常苦恼,他不知道该让谁去,他在寻求一个绝对公平的方式。于是他准备让大家抽签决定,即m个签是去,剩下的是不去。

小A非常想弄明白最后的抽签结果是什么样子的,到底有多少种结果。

请设计一个程序帮助小A。最后输出各种情况的人名即可,一行一种情况,每种情况的名字按照报名即输入顺序排序。

第一行 输入 N M

第二行 到 第 N+1 行 共输入 N 个人名

每种情况输出 M 个人名,空格隔开。

在这里插入图片描述
实际上还是组合型枚举,但是输出元素为人名,我们可以将人名存起来,输出的时候,根据数字下标找到提前存好的人名,直接输出即可。

答案解析:

#include <iostream>
#include <vector>
using namespace std;

int n; //共计N个数
int m; //选m个数
vector<string> name;
vector<string> ans;
vector<int> chosen;
void calc(int x)
{

    if (chosen.size() > m || chosen.size() + (n - x + 1) < m) //剪枝
        return;

    if (x == n + 1)
    { //选够了m个数输出
        string ansTem = "";
        for (int i = 0; i < chosen.size(); i++)
            ansTem += name[chosen[i] - 1] + " ";

        ans.push_back(ansTem);
        return;
    }

    calc(x + 1);
    chosen.push_back(x);

    calc(x + 1);
    chosen.pop_back(); //消除痕迹
}
int main()
{

    cin >> n >> m;
    for (int i = 0; i < n; i++)
    {
        string s;
        cin >> s;
        name.push_back(s);
    }
    calc(1);
    for (int i = ans.size() - 1; i >= 0; i--)
        cout << ans[i] << endl;
}

排列型枚举

在这里插入图片描述

int n; //共计N个数
int order[20];
bool chosen[20];
void calc(int k)
{
    if (k == n + 1)
    {
        for (int i = 1; i <= n; i++)
            cout << order[i] << " ";

        puts("");

        return;
    }
    for (int i = 1; i <= n; i++)
    {
        if (chosen[i])
            continue;
        order[k] = i;
        chosen[i] = 1;
        calc(k + 1);
        chosen[i] = 0;
        order[k] = 0;
    }
}
int main()
{
    cin >> n;
    calc(1);
}

如果问 20 够不够,排列问题是阶乘阶的时间复杂度,如果超过这个复杂度,那么这个题也就不用做了,算不出来。
所以肯定够用。

座次问题

题意:

小 A 的学校,老师好不容易解决了蓝桥杯的报名问题,现在老师又犯愁了。现在有 N 位同学参加比赛,但是老师想给他们排座位,但是排列方式太多了。老师非常想弄明白最后的排座次的结果是什么样子的,到底有多少种结果。

请设计一个程序帮助老师。

最后输出各种情况的人名即可,一行一种情况,每种情况的名字按照报名即输入顺序排序。

第一行 输入 N;
第二行 到 第N+1 行 共输入 N 个人名。

由于小 A 学校承办能力实在有限,所以其中 N 小于等于 10 人。

在这里插入图片描述
代码:

#include <iostream>
#include <vector>
using namespace std;

int n; //共计N个数
vector<string> name;
int order[20];
bool chosen[20];
void calc(int k)
{
    if (k == n + 1)
    {
        for (int i = 1; i <= n; i++)
            cout << name[order[i] - 1] << " ";

        puts("");

        return;
    }
    for (int i = 1; i <= n; i++)
    {
        if (chosen[i])
            continue;
        order[k] = i;
        chosen[i] = 1;
        calc(k + 1);
        chosen[i] = 0;
        order[k] = 0;
    }
}

int main()
{

    cin >> n;
    for (int i = 0; i < n; i++)
    {
        string s;
        cin >> s;
        name.push_back(s);
    }
    calc(1);
}

指数型枚举(自己查)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

懒回顾,半缘君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值