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的变化

31
21
11
32
22
12
33
23
13

(3)写代码

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

#include <iostream>
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的变化

13
23
33
12
22
32
11
21
31
(3)写代码

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

#include <iostream>
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的变化

13
12
11
23
22
21
33
32
3

1

·上下翻转

i的变化

j的变化

31
32
33
21
22
23
11
12
1

3

(3)写代码

·镜面翻转

#include <iostream>
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 <iostream>
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)的关系图:

ldiri1j1i2j2i3j3i4j4
111121314
224232221
331323334
444434241

这里我们可以观察到,灰色的行贪吃蛇都是朝右的,且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 <iostream>
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 <iostream>
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 <iostream>
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;
    }
}

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
邮递员问题(Travelling Salesman Problem,TSP)是一个经典的组合优化问题,也是一个NP难问题。该问题描述为:给定一些城市和每对城市之间的距离,求解访问每一座城市一次并回到起始城市的最短回路。 以下是一个用C++解决TSP问题的例子: ```c++ #include <iostream> #include <vector> #include <algorithm> #include <cmath> using namespace std; const int MAXN = 20; // 最多20个城市 const int INF = 1e9; // 无穷大 int n; // 城市数量 int dist[MAXN][MAXN]; // 距离矩阵 int dp[1 << MAXN][MAXN]; // DP数组 // 计算两个城市之间的距离 int getDist(int i, int j) { int x1 = x[i], y1 = y[i]; int x2 = x[j], y2 = y[j]; return round(sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2))); } // 动态规划解决TSP问题 int tsp() { // 初始化dp数组 for (int i = 0; i < (1 << n); i++) { for (int j = 0; j < n; j++) { dp[i][j] = INF; } } dp[1][0] = 0; // 动态规划 for (int i = 1; i < (1 << n); i++) { for (int j = 0; j < n; j++) { if (i & (1 << j)) { for (int k = 0; k < n; k++) { if (i & (1 << k)) { dp[i][j] = min(dp[i][j], dp[i-(1<<j)][k] + dist[k][j]); } } } } } // 返回结果 int ans = INF; for (int i = 1; i < n; i++) { ans = min(ans, dp[(1<<n)-1][i] + dist[i][0]); } return ans; } int main() { // 输入城市数量和城市坐标 cin >> n; for (int i = 0; i < n; i++) { cin >> x[i] >> y[i]; } // 计算距离矩阵 for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { dist[i][j] = getDist(i, j); } } // 解决TSP问题 int ans = tsp(); // 输出结果 cout << ans << endl; return 0; } ``` 这个例子中,我们使用动态规划算法解决TSP问题。由于TSP问题是一个NP难问题,因此我们无法找到一个多项式时间内解决该问题的算法。不过,动态规划算法是目前最优秀的解决TSP问题的算法之一,它的时间复杂度为O(2^n * n^2)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值