C++矩阵例题分析(1)

一、概念

在计算机编程中,矩阵是一个非常常见的概念,它在各种计算和数据处理任务中发挥着关键作用。在C++中,我们通常使用二维数组来表示矩阵,并使用循环结构进行操作。本文将介绍几个常见的C++矩阵问题,包括旋转和大小控制,以及如何在不使用标准库函数的情况下实现这些操作。

※一定注意,在不会作答的时候不要改了试、改了试,要静下心,重新读题,抓住关键。最好的办法就是画图,可以帮助你高效地理解题目含义和隐藏信息。不要看网上乱七八糟的公式,否则思路中断。相信自己开始的思路。

二、矩阵的旋转

1.顺时针旋转矩阵

(1)画图

顺时针旋转一个矩阵的逻辑看似简单,实际操作还是比较困难的。我们可以尝试不改变数组的值,只改变输出的顺序。这样可以最简洁、最直观地运行正确结果。观察一下未旋转的数字和旋转过的数组:

// 未旋转的数组
// 数字
1 2 3
4 5 6
7 8 9

// 索引
(1,1) (1,2) (1,3)
(2,1) (2,2) (2,3)
(3,1) (3,2) (3,3)



// 旋转后的数组
// 数字
7 4 1
8 5 2
9 6 3

//索引
(3,1) (2,1) (1,1)
(3,2) (2,2) (1,2)
(3,3) (2,3) (1,3)
(2)总结规律

我们可以观察到,每次索引(i,j)的变化应该如下图:

i的变化

j的变化

3 1
2 1
1 1
3 2
2 2
1 2
3 3
2 3
1 3

(3)写代码

i的变化应该是不断减少的,在减少到1的时候会开启下一个循环;j则是每个循环都增加1。知道了这个变化规律以后,我们可以写出如下代码:

#include 
using namespace std;

int main()
{
    int size;
    cin >> size; // 题目保证1<=size<=100
    int matrix[105][105] = {}; // 为了防止下标越界,多开5格存储
    for (int i = 1; i <= size; i++)
    {
        for (int j = 1; j <= size; j++)
        {
            cin >> matrix[i][j]; // 按照习惯,索引直接从1开始
        }
    }
    for (int j = 1; j <= size; j++)
    {
        for (int i = size; i >= 1; i--)
        {
            cout << matrix[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}

2.逆时针旋转矩阵 

(1)画图

一样的方法,换汤不换药。按照之前的方法,先画出图,方便我们理解。

// 未旋转的数组
// 数字
1 2 3
4 5 6
7 8 9

// 索引
(1,1) (1,2) (1,3)
(2,1) (2,2) (2,3)
(3,1) (3,2) (3,3)



// 旋转后的数组
// 数字
3 6 9
2 5 8
1 4 7

//索引
(1,3) (2,3) (3,3)
(1,2) (2,2) (3,2)
(1,1) (2,1) (3,1)
(2)总结规律

索引(i,j)的变化应该是:

i的变化

j的变化

1 3
2 3
3 3
1 2
2 2
3 2
1 1
2 1
3 1
(3)写代码

同样,只是改变输出顺序,并没有改变数组中的值。

#include 
using namespace std;

int main()
{
    int size;
    cin >> size; // 题目保证1<=size<=100
    int matrix[105][105] = {}; // 为了防止下标越界,多开5格存储
    for (int i = 1; i <= size; i++)
    {
        for (int j = 1; j <= size; j++)
        {
            cin >> matrix[i][j]; // 按照习惯,索引直接从1开始
        }
    }
    for (int j = size; j >= 1; j--)
    {
        for (int i = 1; i <= size; i++)
        {
            cout << matrix[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}

3.镜面翻转和上下翻转矩阵

 (1)画图

·镜面翻转

// 未翻转的数组
// 数字
1 2 3
4 5 6
7 8 9

// 索引
(1,1) (1,2) (1,3)
(2,1) (2,2) (2,3)
(3,1) (3,2) (3,3)



// 翻转后的数组
// 数字
3 2 1
6 5 4
9 8 7

//索引
(1,3) (1,2) (1,1)
(2,3) (2,2) (2,1)
(3,3) (3,2) (3,1)

·上下翻转

// 未翻转的数组
// 数字
1 2 3
4 5 6
7 8 9

// 索引
(1,1) (1,2) (1,3)
(2,1) (2,2) (2,3)
(3,1) (3,2) (3,3)



// 翻转后的数组
// 数字
7 8 9
4 5 6
1 2 3

//索引
(3,1) (3,2) (3,3)
(2,1) (2,2) (2,3)
(1,1) (1,2) (1,3)
(2)总结规律

索引(i,j)的规律如下:

·镜面翻转

i的变化

j的变化

1 3
1 2
1 1
2 3
2 2
2 1
3 3
3 2
3

1

·上下翻转

i的变化

j的变化

3 1
3 2
3 3
2 1
2 2
2 3
1 1
1 2
1

3

(3)写代码

·镜面翻转

#include 
using namespace std;

int main()
{
    int size;
    cin >> size; // 题目保证1<=size<=100
    int matrix[105][105] = {}; // 为了防止下标越界,多开5格存储
    for (int i = 1; i <= size; i++)
    {
        for (int j = 1; j <= size; j++)
        {
            cin >> matrix[i][j]; // 按照习惯,索引直接从1开始
        }
    }
    for (int i = 1; i <= size; i++) // 变得慢的放在外面
    {
        for (int j = size; j >= 1; j--) //  变得快的放在里面
        {
            cout << matrix[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}

·上下翻转

#include 
using namespace std;

int main()
{
    int size;
    cin >> size; // 题目保证1<=size<=100
    int matrix[105][105] = {}; // 为了防止下标越界,多开5格存储
    for (int i = 1; i <= size; i++)
    {
        for (int j = 1; j <= size; j++)
        {
            cin >> matrix[i][j]; // 按照习惯,索引直接从1开始
        }
    }
    for (int i = size; i >= 1; i--) // 变得慢的放在外面
    {
        for (int j = 1; j <= size; j++) //  变得快的放在里面
        {
            cout << matrix[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}

三、矩阵的形状变化

从这一部分开始,就是最难的了。一般情况下,我们还是用三件套:画图、总结规律、写代码。也许,你的灵感产生于你的画图,可见画图是多么重要。

现在我们来看几个基础的矩阵形状变化的例题。

1.贪吃蛇矩阵

(1)了解题意,画图

贪吃蛇矩阵,就像贪吃蛇一样,会以标准的90度角扭来扭去。比如说:

1 2 3 4
8 7 6 5
9 10 11 12
16 15 14 13

这里,我们可以看到,有一条贪吃蛇从索引(1,1)的位置开始,向右扭到(1,4),然后向下走一个坐标。接着向左扭到(2,1),再向下一格……以此类推。

一样的,反之字矩阵就是下面的样子(其实就是贪吃蛇矩阵的变形):

16 15 14 13
9 10 11 12
8 7 6 5
1 2 3 4
(2)总结规律

下面是行数(l)、方向(dir)和索引(i,j)的关系图:

l dir i1 j1 i2 j2 i3 j3 i4 j4
1 1 1 1 2 1 3 1 4
2 2 4 2 3 2 2 2 1
3 3 1 3 2 3 3 3 4
4 4 4 4 3 4 2 4 1

这里我们可以观察到,灰色的行贪吃蛇都是朝右的,且j1至j4是不断增加的;白色的行贪吃蛇都是朝左的,且j1至j4是不断减少的。根据这个规律,可以列出公式:

1. 在l ≡ 1 (mod 2)的情况下,dir为"右";否则为"左"。
2. 在l ≡ 1 (mod 2)的情况下,j(n) = j(n-1) + 1;否则j(n) = j(n-1) - 1。
3. 无论如何,i永远等于l。

其中,l ≡ 1 (mod 2)意思是,l除以2后余数为1,也就是行数为基数的情况下。

所以,我们就得到综合式:

l ≡ 1 (mod 2), dir = "右", j(n) = j(n-1) + 1

l ≡ 0 (mod 2), dir = "左", j(n) = j(n-1) - 1

(3)写代码

根据综合式,我们可以尝试写出代码。

#include 
using namespace std;

int main()
{
    int size;
    cin >> size;
    int matrix[105][105] = {};

    int number = 1;
    for (int i = 1; i <= size; i++)
    {
        for (int j = 1; j <= size; j++)
        {
            if (i % 2 == 1)
            {
                matrix[i][j] = number;
            }
            else
            {
                matrix[i][size+1-j] = number;
            }
            number++;
        }
    }
    
    for (int i = 1; i <= size; i++)
    {
        for (int j = 1; j <= size; j++)
        {
            cout << matrix[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}

代码的核心在存储的过程中。首先要判断所在的行是不是奇数,是就从这个一位数组(也就是这一行)的第一个元素到最后一个元素进行正序存储,否则从第一个元素到最后一个元素进行倒序存储。

反之字矩阵也是一样:

#include 
using namespace std;

int main()
{
    int size;
    cin >> size;
    int matrix[105][105] = {};

    int number = size * size;
    for (int i = 1; i <= size; i++)
    {
        for (int j = 1; j <= size; j++)
        {
            if (i % 2 == 1)
            {
                matrix[i][j] = number;
            }
            else
            {
                matrix[i][size+1-j] = number;
            }
            number--;
        }
    }
    
    for (int i = 1; i <= size; i++)
    {
        for (int j = 1; j <= size; j++)
        {
            cout << matrix[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}

2.扩张式矩阵

(1)了解题意,画图

例如,输入3,输出:

1 1 1 1 1
1 2 2 2 1
1 2 3 2 1
1 2 2 2 1
1 1 1 1 1

看起来非常难,但是我们可以总结出数字开始的索引:

1: (1,1)
2: (2,2)
3: (3,3)
…
(2)总结规律

根据数字和索引的规律,我们可以发现数字number开始的位置应该是(number,number)。知道了这个规律,我们就有了思路:重复地按照范围填充数字。比如说1,按照输入3的例子,它的范围是5*5,2是3*3,3是1*1。所以,我们可以增加一个范围变量,来存储其所在的范围。

(3)写代码
#include 
using namespace std;

int main() {
    int n;
    cin >> n;
    int matrix[209][209] = {}; // 数组的宽度是1+(n-1)*2

    int number = 1;
    int scope = n;
    while (number <= n)
    {
        for (int i = number; i <= n-number; i++) // i从number开始
        {
            for (int j = number; j <= n-number; j++) // j也从number开始
            {
                matrix[i][j] = number;
            }
        }
        number++; // 每次存储完数字,都要存储下一个数字
        scope--; // 范围变得更小
    }
    
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            cout << matrix[i][j] << " ";
        }
        cout << endl;
    }
}

以上就是一个可以实现生成回字形矩阵的代码。

这是我第一次写博客,如果有什么错误,可以及时评论纠正。感谢浏览!下期会讲更难的矩阵哦,尽情期待~

你可能感兴趣的:(c++,算法,笔记,矩阵)