螺旋矩阵 之二
问题
1 按顺时针方向构建(或螺旋访问)一个n * n的螺旋矩阵,效果见下图。
2 在不构造螺旋矩阵的情况下,给定坐标i、j值求其对应的值f(i, j)。
比如对6 * 6矩阵, f(2, 0) =19 f(2, 1) = 6
思路一
前一篇文章已经讨论了一类螺旋矩阵(由外向内),而这一类螺旋矩阵,则是由内向外扩散。这两类矩阵可以通过下面的方法相互转换。
由于是 n * n矩阵,对坐标(x,y)落在矩形的哪一条边上,可以直接使用x <= y进行判断,原来的代码可以优化为:
{
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):
(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 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);
}
完整测试代码:
//www.cnblogs.com/flyinghearts
#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";
}
}
作者: flyinghearts
出处: http://www.cnblogs.com/flyinghearts/
本文采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。