【算法详解】:从 模拟 开始打开算法密匙

在这里插入图片描述

🫧 励志不掉头发的内向程序员个人主页

 ✨️ 个人专栏: 《C++语言》《Linux学习》

🌅偶尔悲伤,偶尔被幸福所完善


👓️博主简介:

在这里插入图片描述


前言

Hi,大家好,我们将在这里开启我们的算法新篇章。学习算法是一件长期不间断的事情,不管是什么算法,容易也好,困难也罢,我们都必须认真的去了解学习。本章节我们将=将来讲解我们最基本的算法—模拟算法,我们一起来看看吧。

在这里插入图片描述


一、算法简介

模拟,顾名思义就是题目让你做什么,你就做什么。考察的是将思路转化成代码的能力已经代码的控制能力。

注意:
模拟算法是所有算法的基础,但是我们得先明白一个点,基础算法不意味着简单算法,只是说明这个算法的实用性比较广,是学习算法的基石,所以我们在学习时不能轻视基础算法,它是你的算法大厦建多高的低级。

二、算法原理剖析

模拟没有什么算法原理,主要就是见招拆招,题目怎么说,你就怎么做。模拟题目十分考验我们的代码能力,我们来看几道题来理解我们的模拟算法吧。

三、实践算法原理

3.1、多项式输出

题目来源:洛谷

题目链接:P1067 [NOIP 2009 普及组] 多项式输出 - 洛谷

题目难度:❤️

(1)题目详情

【题目描述】
在这里插入图片描述
【输入格式】
在这里插入图片描述
【输出格式】
在这里插入图片描述
【示例一】
在这里插入图片描述
【示例二】
在这里插入图片描述

(2)题目解析
看到这么长的题目,遇到吃压力的朋友可能就直接跑路了,但是其实仔细看看题目,我们就会发现其实并不是特别难,只要一步一步跟着题目的意思走就可以。
我们规定 1: 要让多项式中自变量为 x,从左到右按照次数递减顺序给出多项式。我们可以通过一个for 循环来解决这个问题,让我们的 for 循环从最大值一直减到 0 即可。

 int n; cin >> n;
 for (int i = n; i >= 0; i--)
 {}

我们规定 2:多项式只包含系数不为 0 的项。我们可以用一个条件语句,让我们的系数如果是 0 就continue。

 int a; cin >> a;
 if (a == 0) continue;

我们规定 3/4/5:
此时我们如果把我们的符号和数字当作一个整体的话,这个代码也可以实现,但是会多很多的条件语句。我们要记住,像这种输出的题,这些所以的数字、正数、负数等,本质上都是一个一个字符串,我们可以把它们一个一个输出而非整体输出,一个一个输出会比整体输出的代码好控制。

 if (a < 0) cout << '-';
 else if (i != n) cout << '+';
 a = abs(a);
 if (a != 1 || (a == 1 && i == 0)) cout << a;
 if (i == 0) continue;
 else if (i == 1) cout << 'x';
 else cout << "x^" << i;

(3)代码实现

int main()
{
    int n; cin >> n;
    for (int i = n; i >= 0; i--)
    {
        int a; cin >> a;
        if (a == 0) continue;
        if (a < 0) cout << '-';
        else if (i != n) cout << '+';
        a = abs(a);
        if (a != 1 || (a == 1 && i == 0)) cout << a;
        if (i == 0) continue;
        else if (i == 1) cout << 'x';
        else cout << "x^" << i;
    }
    return 0;
}

3.2、蛇形方阵

题目来源:洛谷

题目链接:P5731 【深基5.习6】蛇形方阵

题目难度:❤️

(1)题目详情

【题目描述】
在这里插入图片描述
【输入格式】
在这里插入图片描述
【输出格式】
在这里插入图片描述
【示例一】
在这里插入图片描述


(2)题目解析
此题也是模拟题,我们依照它的说法来模拟,首先他给我们一个数字 n,此时我们要输出一个 n * n 的蛇形方阵,这个方阵从左上角开始顺时针填入1、2、3、4…。题目不难理解,但是我们想要模拟时就出现问题了,我们怎么进行顺时针填数字呢?其实在我们算法中,按照一定规律填数有一个通用的解法,那就是定义一个方向向量:
比如此题一共有 4 个方向,分别是右、下、左、上,分别对应的是(0,1)、(1,0)、(0,-1),(-1,0)。这个意思就是jiar假如我们把数据存储在 a[n][n] 中,我们(0,1)就相当于a[x + 0][y + 1],它与a[x][y] 相比往右移了一个,其余的是同理,此时我们分别用两个数组存储我们的方向向量。

// 定义 右,下,左,上 四个⽅向
int dx[] = {0, 1, 0, -1};
int dy[] = {1, 0, -1, 0};

我们 dx 就是是决定我们的纵坐标的位移,dy 就是决定横坐标的位移。我们调用这个方向向量就能让我们的编译器按照我们想要的方向运行了。
我们从一个方向跳转到另一个方向的条件就是如果下一个方向是填过数字的,那就换一个方向,此时我们一直重复,越界了就走下一个方向,即可完成。


(3)代码实现

#include <iostream>
using namespace std;
const int N = 15;
// 定义 右,下,左,上 四个⽅向
int dx[] = { 0, 1, 0, -1 };
int dy[] = { 1, 0, -1, 0 };
int arr[N][N];
int main()
{
    int n; cin >> n;
    // 模拟填数过程
    int x = 1, y = 1; // 初始位置
    int cnt = 1; // 当前位置要填的数
    int pos = 0; // 当前的⽅向
    while (cnt <= n * n)
    {
        arr[x][y] = cnt;
        // 计算下⼀个位置
        int a = x + dx[pos], b = y + dy[pos];
        // 判断是否越界
        if (a < 1 || a > n || b < 1 || b > n || arr[a][b])
        {
            // 更新出正确的该⾛的位置
            pos = (pos + 1) % 4;
            a = x + dx[pos], b = y + dy[pos];
        }
        x = a, y = b;
        cnt++;
    }
    // 输出
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            printf("%3d", arr[i][j]);
        }
        puts("");
    }
    return 0;
}



3.3、字符串展开

题目来源:洛谷

题目链接:P1098 [NOIP 2007 提高组] 字符串的展开

题目难度:❤️

(1)题目详情

【题目描述】
在这里插入图片描述
【输入格式】
在这里插入图片描述
【输出格式】
在这里插入图片描述
【示例一】
在这里插入图片描述
【示例二】
在这里插入图片描述


(2)题目解析
根据规则模拟字符串展开的过程即可。题目的规则较长,但是叙述的非常详细,只要把细节考虑到位,分类讨论就能搞定。在代码比较复杂的题目里面,我们可以尽量把经常出现的功能去写成函数,把各种题目所要求的功能写成函数把它抽离开来,此时我们再去实现我们的代码只需要去调用函数即可。


(3)代码实现

#include <iostream>
#include <algorithm>
using namespace std;
int p1, p2, p3, n;
string s;
string ret;
// 判断是否是数字字符
bool isdig(char ch)
{
    return ch >= '0' && ch <= '9';
}
// 判断是否是⼩写字⺟
bool islet(char ch)
{
    return ch >= 'a' && ch <= 'z';
}
// 把 [left, right] 之间的字符展开
// left, right 这两个字符是不做处理
void add(char left, char right)
{
    string t;
    // 遍历中间的字符
    for (char ch = left + 1; ch < right; ch++)
    {
        char tmp = ch;
        // 处理 p1
        if (p1 == 2 && islet(tmp)) tmp -= 32; // ⼩写变⼤写
        else if (p1 == 3) tmp = '*'; // 变成星号
        // 处理 p2
        for (int i = 0; i < p2; i++)
        {
            t += tmp;
        }
    }
    // 处理 p3
    if (p3 == 2) reverse(t.begin(), t.end());
    ret += t;
}

int main()
{
    cin >> p1 >> p2 >> p3 >> s;
    n = s.size();
    for (int i = 0; i < n; i++)
    {
        char ch = s[i];
        if (s[i] != '-' || i == 0 || i == n - 1) ret += ch;
        else
        {
            char left = s[i - 1], right = s[i + 1];
            // 判断是否展开
            if (isdig(left) && isdig(right) && right > left ||
                islet(left) && islet(right) && right > left)
            {
                // 展开
                add(left, right);
            }
            else
            {
                ret += ch;
            }
        }
    }
    cout << ret << endl;
    return 0;
}



总结

我们模拟的题型是非常多的,因为它本质上就是考察你的代码能力,在以后的日子里,我们随着题目刷的足够的多,我们代码能力也会越强,此时模拟对于我们来说也就会越简单,所以本章节只是介绍了一下模拟是什么,真正的练习是我们以后的不断刷题去提升我们的代码能力。

在这里插入图片描述

🎇坚持到这里已经很厉害啦,辛苦啦🎇
ʕ • ᴥ • ʔ
づ♡ど
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值