MxN螺旋矩阵(由内向外)

问题描述

按顺时针方向构建一个m * n的螺旋矩阵(或按顺时针方向螺旋访问一个m * n的矩阵):在不构造螺旋矩阵的情况下,给定坐标ij值求其对应的值f(i, j)。如6 * 6矩阵, f(2, 0) =19  f(2, 1) = 6。


方案一

问题有两个

1. 编程实现输出这个矩阵问题,我是采用模拟进行构造的,可以看到从1开始的方向变化始终是 right->down->left->up,所持续走的长度为1->1->2->2->3->3->...,发现了这个规律不难写出代码了!注意下面我把1的位置设置 在((n-1)/2, (n-1)/2)的位置。

void Simulate(int n) {
    int x, y;
    x = y = (n - 1) / 2; //1的位置
    data[x][y] = 1;
    int len = 1;
    int count = 0;
    int num = 2;
    DIRECTION dir = RIGHT;
    while(num  <= n * n) {
        for(int i = 0; i < len; i++){
           switch(dir){
            case LEFT:
                --y;    break;
            case RIGHT:
                ++y;     break;
            case UP:
                --x;    break;
            case DOWN:
                ++x;    break;
            default:    break;
            }
            data[x][y] = num++;
        }
        count++;
        if(count == 2) {
            count = 0;
            len++;    
        }
        dir = (DIRECTION)((dir+1)%4);
    }
}
2. 设1点的坐标是(0,0),x方向向右为正,y方向向下为正.例如:7的坐标为(-1,-1) ,2的坐标为(0,1),3的坐标为(1,1).编程实现输入任意一点坐标(x,y),输出所对应的数字。先找出规律,然后进行模拟。首先,不难看出n*n的螺旋矩阵的右下角的坐标一定是(m, m),这里m=n-1 通过观察,可以看出 n=1的时候,右下角(0,0)的值为1,当n=2的时候,右下角(1,1)的坐标值为(3,3),当n=3的时候,右下角(2,2)的坐标值为13.直觉告诉我,这个值是关于n的二次函数,设f(n) = a*n^2+b*n+c 联立方程组,可以求得a,b,c。 最终算出来的f(n) = 4*n^2-2*n + 1。下面再根据(x,y)和右下角(n-1,n-1)之间的关系,计算出值即可。这里要注意当x的值与n-1相同时,应优先考虑y与-m是否有联系。这就要求在函数中要注意x,y的判断先后顺序了。

代码如下:

//以(1,1)所在位置作为原点,向右作为x正半轴,向下作为y正半轴
int GetValue(int x, int y)
{
    int m = max(abs(x), abs(y));
    int rightBottom = m*m*4-2*m+1;
    int value = 0;
    if(x == -m){
        value = rightBottom +2*m+m-y;
    }
    else if(y == m){
        value = rightBottom+m-x;
    }
    else if(y == -m){
        value = rightBottom+4*m+x+m;
    }
    else if(x == m){
        value = rightBottom-(m-y);
    }
    return value;
}

完整代码如下:

#include <iostream>
#include <cstdlib>
#include <algorithm>

using namespace std;

const int N = 100;

int data[N + 1][N + 1];

enum DIRECTION
{
    RIGHT, DOWN , LEFT, UP
};

//模拟整个过程
void Simulate(int n)
{
    int x, y;
    x = y = (n-1)/2; //1的位置
    data[x][y] = 1;
    int len = 1;
    int count = 0;
    int num = 2;
    DIRECTION dir = RIGHT;
    while(num <= n*n){
        for(int i = 0; i < len; i++){
            switch(dir){
            case LEFT:
                --y;    break;
            case RIGHT:
                ++y;     break;
            case UP:
                --x;    break;
            case DOWN:
                ++x;    break;
            default:    break;
            }
            data[x][y] = num++;
        }
        count++;
        if(count == 2){
            count = 0;
            len++;    
        }
        dir =(DIRECTION)((dir + 1)%4);
    }
}

//打印螺旋矩阵
void Output(int n){
    int i, j;
    for(i = 0; i < n; i++){
        cout << data[i][0];
        for(j = 1; j < n; j++)
            cout << "\t" << data[i][j];
        cout << endl;
    }
}

//以(1,1)所在位置作为原点,向右作为x正半轴,向下作为y正半轴
int GetValue(int x, int y){
    int m = max(abs(x), abs(y));
    int rightBottom = m*m*4-2*m+1;
    int value = 0;
    if(x == -m){
        value = rightBottom+2*m+m-y;
    }
    else if(y == m){
        value = rightBottom+m-x;
    }
    else if(y == -m){
        value = rightBottom+4*m+x+m;
    }
    else if(x == m){
        value = rightBottom-(m-y);
    }
    return value;
}

void TestPos(int n){
    int i, j;
    for(i = 0; i < n; i++){
        cout << GetValue(0-(n-1)/2,i-(n-1)/2);
        for(j = 1; j < n; j++)
            cout << "\t" << GetValue(j-(n-1)/2,i-(n-1)/2);
        cout << endl;
    }
}

int main(){
    int n;
    while(cin >> n){
        if(n <= 0 || n > 100){
            cerr << "Size error!" << endl;
            break;
        }
        else{
            Simulate(n);
            Output(n);
            cout << endl;
            TestPos(n);
        }
    }
    return 0;
}

方案二

MxN螺旋矩阵(由内向外),而这一类螺旋矩阵,则是由内向外扩散。这两类矩阵可以通过下面的方法相互转换.由于是 n * n矩阵,对坐标(xy)落在矩形的哪一条边上,可以直接使用x <= y进行判断,

原来的代码可以优化为:

int getv(int x, int y, int n) // 由外内向顺时针螺旋
{
  if (x <= y) {
    int k = min(x,n-1-y);
    return  4*k*(n-k)+1+(x+y-k*2);
  }
  int k = min(y, n-1-x)+1; 
  return 4*k*(n-k)+1-(x+y-(k-1)*2);
}

int getv_in(int x, int y, int n) // 由内向外顺时针螺旋
{
  if (n & 1) return n*n+1-getv(x,n-1-y,n);
  return n*n+1-getv(n-1-x,y,n);
}
将矩阵按1,1,2,2, … n-1,n-1, n 个数划分成几个矩形,比如:7*7 矩阵 :(1 2 3 4 5 6 6 个点构成矩形0,(7 8 9 10 11 12 13 14 15 16 )(17 18 19 20 构成矩形1 , 21 22 23 24 25 )(26 27 28 29 30 )(31 32 33 34 35 36 )(37 38 39 40 41 42 )构成矩形2, 43 44 45 46 47 48 49 构成矩形3 的一条. 若对第k k=0, 1, 2 … )个矩形,起始点坐标为(i, i) ,则 i + k = floor((n-1)/2). 其右上角顶点坐标为( i, i + 2 * k + 1 ).设 t = 2 * floor((n-1)/2) + 1 = (n - 1) | 1 则右上角顶点坐标为:(i, t - i). k k=0, 1, 2 … )个矩阵的4 个顶点为(注意起始点不是左上角顶点而是(i, i) ):.

(i, i-1) ----------------------------------------- (i, t-i)

|                                                   |

|                                                   |

|                                                   |

(t-i, i-1) ----------------------------------------- (t-i, t-i) 

对给定的坐标(x,y),如果它落在某个这类矩形上,显然其所在的矩形起始点横坐标i满足:i = min{x, y+1, t-x, t-y}.第k个矩形内的所有点构成(2*k+2)*(2*k+3)矩阵,共有元素P(k)=(2*k+2)*(2*k+3)个,第k个矩形的起始点(i,i)对应的值为T(i)=P(k-1)+1=2*k*(2*k+1)+1=(t-2*i)*(t-2*i-1)+1.对某个矩形,设矩形上的点(x, y)到起始点(i,i)的距离d = x-i + y-i = x+y-2*i,设点(x, y)到下一起始点(i-1,i-1)的距离为dd,则 dd = d + 2.① 向右和向下都只是横坐标或纵坐标增加1,这两条边上的点满足f(x, y) = T(i) + d ② 向左和向下都只是横坐标或纵坐标减少1,这两条边上的点满足f(x, y) = T(i-1) –dd. 对矩阵的构建和另一种螺旋矩阵类似

构建矩阵代码:

#include<cstdio>
const int N = 128;
int a[N][N];

void set_1a(int n)
{
  const int nn = (n - 1) | 1;
  int x = nn / 2u, y = x, k = 0;
  for (int len = 1; len < nn; len += 2) {
    for (int j = 0; j < len; ++j) a[x][y++] = ++k;
    for (int j = 0; j < len; ++j) a[x++][y] = ++k;
    for (int j = 0; j <= len; ++j) a[x][y--] = ++k;
    for (int j = 0; j <= len; ++j) a[x--][y] = ++k;
  }
  
  for(int j = 0; j < nn; ++j) a[0][j] = ++k;
  if ((n & 1) == 0) {
    for (int j = 0; j < nn; ++j) a[j][nn] = ++k;
    for (int j = nn; j >= 0; --j) a[nn][j] = ++k;  
  }
}

void set_1b(int n)
{
  int len = 0, k = 0, x = (n - 1) / 2u, y = x;
  while (1) {
    ++len;
    for (int j = 0; j < len; ++j) a[x][y++] = ++k;
    if (len == n) break;
    for (int j = 0; j < len; ++j) a[x++][y] = ++k;

    ++len;
    for (int j = 0; j < len; ++j) a[x][y--] = ++k;
    if (len == n) break;
    for (int j = 0; j < len; ++j) a[x--][y] = ++k;    
  }
}

void set_2(int n)
{
  const int nn = (n - 1) | 1;
  int k = 0;
  for (int i = nn / 2u; i > 0; --i) {
    const int C = nn - i;
    for (int j = i; j < C; ++j) a[i][j] = ++k;    
    for (int j = i; j < C; ++j) a[j][C] = ++k;    
    for (int j = C; j >= i; --j) a[C][j] = ++k;    
    for (int j = C; j >= i; --j) a[j][i - 1] = ++k;
  }
  
  for(int j = 0; j < nn; ++j) a[0][j] = ++k;
  if ((n & 1) == 0) {
    for (int j = 0; j < nn; ++j) a[j][nn] = ++k;
    for (int j = nn; j >= 0; --j) a[nn][j] = ++k;  
  }
}

void print(int n){
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < n; ++j)
      printf("%3d ",a[i][j]);
    printf("\n");
  } 
  printf("\n");  
}

int main(){
  for (int i = 3; i < 9; ++i) {
    set_1b(i);
    print(i);
  }
}

给定坐标求值的代码

int getv(int x, int y, int n){ //螺旋矩阵(由内向外扩散)
  int t = (n-1)|1;
  if (x <= y) {
    int k = min(x,t-y);
    return (t-2*k)*(t-2*k-1)+1+(x+y-2*k);
  }
  int k = min(y+1,t-x)-1;
  return (t-2*k)*(t-2*k-1)+1-(x+y-2*k);
}

完整测试代码:

//螺旋矩阵(由内向外扩散),给定坐标直接求值
#include<iostream>
#include<algorithm>
using std::min;
using std::cout;
 
int getv(int x, int y, int n) //螺旋矩阵(由内向外扩散)
{
 int t = (n-1)|1;
  if (x <= y) {
    int k = min(x, t-y);
    return (t-2*k)*(t-2*k-1)+1+(x+y-2*k);
  }
  int k = min(y + 1, t - x) - 1;
  return (t-2*k)*(t-2*k-1)+1-(x+y-2*k);
} 
 
int main(){
  const int M = 12;
  for (int k = 2; k < M; ++k) {
    for (int i = 0; i < k; ++i) {
      for (int j = 0; j < k; ++j) {
        cout.width(4);
        cout << getv(i, j, k) << " ";
      }
      cout << "\n"; 
    }
    cout << "\n";
  }
}
============================================================
转载请注明出处http://blog.csdn.net/utimes/article/details/8779273
============================================================
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值