2022年10月2日,旋转矩阵的三种方法

D. 旋转棋盘

题目描述

在这里插入图片描述

输入样例

5 5 3
0 1 2 3 4
0 1 2 3 4
0 1 2 3 4
0 1 2 3 4
0 1 2 3 4
1 1 2
1 2 2
2 1 3

输出样例

0 1 0 3 4
0 0 1 3 4
1 1 2 3 4
2 2 2 3 4
0 1 2 3 4

数据范围

对于30%的数据,1<N, M ,Q ≤ 100;

对于另外30%的数据,保证

两之间不相交或相等;

对于100%的数据,1≤N , M,Q ≤2000。所有数取值都为0~9。

解题思路

  1. 模拟解法:顺时针旋转90deg 等价于先进性转置,再进中心对称的列转化。这样可以过30% 的数据。

在这里插入图片描述

  1. 第二种也是模拟解法, 把对应的三角区域的数据沿着顺时针方向旋转 。 这样速度可以加快50%。 因为遍历的数据少了只有四分之一矩阵。 (旋转矩阵3.png)]
#include <algorithm>
#include <cstring>
#include <iostream>
#include <stdio.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
int a[2002][2002];
inline int read()
{
    int x = 0, f = 1;
    char s = getchar();
    while (s < '0' || s > '9')
    {
        if (s == '-')
            f = -f;
        s = getchar();
    }
    while (s >= '0' && s <= '9')
    {
        x = (x << 3) + (x << 1) + (s ^ 48);
        s = getchar();
    }
    return x * f;
}


void rotate(int x, int y, int c)// 旋转矩阵第一种写法。
{
    // 矩阵转置(行和列互换)
    for (int i = 0; i <= c - 1; i++)
    {
        for (int j = 0; j <= i; j++)
            swap(a[i + x][j + y], a[j + x][i + y]);
    }
    //矩阵中心对称交换,即第一列和最后一列交换
    for (int i = 0; i <= c - 1; i++)
        for (int j = 0; j <= (c >> 1) - 1; j++)
        {
            int temp = a[i + x][j + y];
            a[i + x][j + y] = a[i + x][c - 1 + y - j];
            a[i + x][c - 1 + y - j] = temp;
        }
}

void work(int x, int y, int c) // 旋转矩阵第二种写法。 
{
    int N = c - 1;
    for (int i = 0; i < c / 2; i++)
        for (int j = i; j < N - i; j++)
        {
            int temp = a[i + x][j + y];
            a[i + x][j + y] = a[N - j + x][i + y];
            a[N - j + x][i + y] = a[N - i + x][N - j + y];
            a[N - i + x][N - j + y] = a[j + x][N - i + y];
            a[j + x][N - i + y] = temp;
        }
}

int main()
{
    freopen("rotate.in", "r", stdin);   //输入重定向,目录下的in.txt文件中读取
    freopen("rotate.out", "w", stdout); //输出重定向,目录下的out.txt文件中

     输入
    int n = read(), m = read(), q = read();
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            a[i][j] = read();

     操作 
    while (q--)
    {
        int x = read(), y = read(), c = read();
        rotate(x, y, c);
    }

     输出
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
            printf("%d ", a[i][j]);
        printf("\n");
    }

    return 0;
}

终极解法:

使用链表。

由于旋转之后,虽然方块内数的上下左右变化了,但是他们的相对位置不变,仅有正方形的边上的数改变了原来连接的数,那么我们只要改变这些数即可。

在链表上访问时,我们可以通过两个数确定一条直线,即不断从一个数向另一个数的方向走,这样就能访问到确定位置的数。

Coding…!

#include <algorithm>
#include <cstring>
#include <iostream>
#include <stdio.h>
using namespace std;
const int N = 2005, M = 5e6 + 5;
int p[M][4], ans[N][N], g[N][N];
int n, m, Q, val[M], a[8], b[8];

inline void Search(int id, int stp, int &A, int &B)
{
    int now = p[id][0], pre = id;
    for (int i = 1; i <= stp; ++i)
    {
        for (int j = 0; j < 4; ++j)
            if (p[now][j] == pre)
            {
                pre = now;
                now = p[now][j ^ 1];
                break;
            }
    }
    A = pre;
    B = now;
}

inline int read()
{
    int x = 0, f = 1;
    char s = getchar();
    while (s < '0' || s > '9')
    {
        if (s == '-')
            f = -f;
        s = getchar();
    }
    while (s >= '0' && s <= '9')
    {
        x = (x << 3) + (x << 1) + (s ^ 48);
        s = getchar();
    }
    return x * f;
}

int main()
{
    // freopen("rotate.in", "r", stdin);   //输入重定向,
    // freopen("rotate.out", "w", stdout); //输出重定向,

    n = read(), m = read(), Q = read();

    // 给位置编号,
    for (int i = 0, im = n + 1; i <= im; ++i)
        for (int j = 0, jm = m + 1; j <= jm; ++j)
            g[i][j] = i * (m + 2) + j;

    // 初始化链表
    for (int i = 1; i <= m; ++i)
    {
        p[g[0][i]][0] = g[1][i];
        p[g[0][i]][2] = g[0][i - 1];
        p[g[0][i]][3] = g[0][i + 1];
        p[g[n + 1][i]][0] = g[n][i];
        p[g[n + 1][i]][2] = g[n + 1][i - 1];
        p[g[n + 1][i]][3] = g[n + 1][i + 1];
    }
    for (int i = 1; i <= n; ++i)
    {
        p[g[i][0]][0] = g[i][1];
        p[g[i][0]][2] = g[i - 1][0];
        p[g[i][0]][3] = g[i + 1][0];
        p[g[i][m + 1]][0] = g[i][m];
        p[g[i][m + 1]][2] = g[i - 1][m + 1];
        p[g[i][m + 1]][3] = g[i + 1][m + 1];
    }
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
        {
            val[g[i][j]] = read();
            p[g[i][j]][2] = g[i - 1][j];
            p[g[i][j]][3] = g[i + 1][j];
            p[g[i][j]][0] = g[i][j - 1];
            p[g[i][j]][1] = g[i][j + 1];
        }

    // 处理数据
    int x, y, c;
    while (Q--)
    {
        x = read(), y = read(), c = read();
        if (c == 1)
            continue;
        // 对边界进行处理
        Search(g[x][0], y - 1, a[0], b[0]);
        Search(g[x + 1][0], y - 1, a[4], b[4]);

        Search(g[0][y + c - 1], x - 1, a[1], b[1]);
        Search(g[0][y + c - 2], x - 1, a[5], b[5]);

        Search(g[x + c - 1][m + 1], m - y - c + 1, a[2], b[2]);
        Search(g[x + c - 2][m + 1], m - y - c + 1, a[6], b[6]);

        Search(g[n + 1][y], n - x - c + 1, a[3], b[3]);
        Search(g[n + 1][y + 1], n - x - c + 1, a[7], b[7]);

        for (int i = 1; i <= c; ++i)
        {
            for (int j = 0; j < 4; ++j)
            {
                for (int k = 0; k < 4; ++k)
                    if (p[a[j]][k] == b[j])
                    {
                        p[a[j]][k] = b[!j ? 3 : j - 1];
                        break;
                    }
                for (int k = 0; k < 4; ++k)
                    if (p[b[j]][k] == a[j])
                    {
                        p[b[j]][k] = a[j == 3 ? 0 : j + 1];
                        break;
                    }
            }
            for (int j = 0; j < 4; ++j)
            {
                for (int k = 0; k < 4; ++k)
                    if (p[a[j + 4]][k] == a[j])
                    {
                        a[j] = a[j + 4];
                        a[j + 4] = p[a[j + 4]][k ^ 1];
                        break;
                    }
                for (int k = 0; k < 4; ++k)
                    if (p[b[j + 4]][k] == b[j])
                    {
                        b[j] = b[j + 4];
                        b[j + 4] = p[b[j + 4]][k ^ 1];
                        break;
                    }
            }
        }
    }

    // 处理最后数组

    for (int i = 1; i <= n; ++i)
    {
        int pre = g[i][0], now = p[pre][0];
        for (int j = 1; j <= m; ++j)
        {
            ans[i][j] = val[now];
            for (int k = 0; k < 4; ++k)
                if (p[now][k] == pre)
                {
                    pre = now;
                    now = p[now][k ^ 1];
                    break;
                }
        }
    }

    // 输出数据
    for (int i = 1; i <= n; ++i, putchar('\n'))
        for (int j = 1; j <= m; ++j)
            putchar(ans[i][j] + '0'), putchar(' ');

    //fclose(stdin);
    //fclose(stdout);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值