螺旋矩阵,两步进阶,从暴力到o(1)

题目描述

一个 n 行 n 列的螺旋矩阵可由如下方法生成:

从矩阵的左上角(第 1 行第 1 列)出发,初始时向右移动;如果前方是未曾经过的格子,则继续前进,否则右转;重复上述操作直至经过矩阵中所有格子。根据经过顺序,在格子中依次填入 1, 2, 3, ... , n ,便构成了一个螺旋矩阵。
下图是一个 n = 4 时的螺旋矩阵。
1 2
3 4
12 13 14 5
11 16 15 6
10 9 8 7

现给出矩阵大小 n 以及 i 和 j ,请你求出该矩阵中第 i 行第 j 列的数是多少。


输入描述:

输入共一行,包含三个整数 n,i,j ,每两个整数之间用一个空格隔开,分别表示矩阵大小、待求的数所在的行号和列号。

输出描述:

输出一个整数,表示相应矩阵中第 i 行第 j 列的数。

输入

4 2 3

输出

14

备注:

对于 50% 的数据, 1 ≤ n ≤ 100 ;
对于 100% 的数据, 1 ≤ n ≤ 30,000,1 ≤ i ≤ n,1 ≤ j ≤ n

链接:https://ac.nowcoder.com/acm/problem/16502
来源:牛客网

1.暴力模拟法,o(n*2),只能通过50%的数据。

#include<iostream>
using namespace std;

/***********************
1<=n<=30000无法用数组存储
***********************/

int n,i,j;
int top_bound,bottom_bound,left_bound,right_bound;
int count=1;
int direction=0;
 
int main(){
    cin>>n>>i>>j;
    int k=1,m=1;
    top_bound=1;
    bottom_bound=n+1;
    left_bound=0;
    right_bound=n+1;
    while(count<=n*n){
        switch(direction){
            case 0:
                //cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
                if(m==right_bound){
                    //cout<<"reach right_bound "<<right_bound<<" change direction from "<<direction<<" ";
                    direction=(direction+1)%4;
                    right_bound--;
                    m--;
                    k++;
                    //cout<<"to "<<direction<<endl;
                    continue;
                }
                if(k==i&&m==j){
                    cout<<count<<endl;
                    return 0;
                }
                m++;
                count++;
                break;
             
            case 1:
                //cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
                if(k==bottom_bound){
                    //cout<<"reach bottom_bound "<<bottom_bound<<" change direction from "<<direction<<" ";
                    direction=(direction+1)%4;
                    bottom_bound--;
                    k--;
                    m--;
                    //cout<<"to "<<direction<<endl;
                    continue;
                }
                if(k==i&&m==j){
                    cout<<count<<endl;
                    return 0;
                }
                k++;
                count++;
                break;
                 
             
            case 2:        
                //cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
                if(m==left_bound){
                    //cout<<"reach left_bound "<<left_bound<<" change direction from "<<direction<<" ";
                    direction=(direction+1)%4;
                    left_bound++;
                    m++;
                    k--;
                    //cout<<"to "<<direction<<endl;
                    continue;
                }
                if(k==i&&m==j){
                    cout<<count<<endl;
                    return 0;
                }
                m--;
                count++;
                break;
             
            case 3:
                //cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
                if(k==top_bound){      
                    //cout<<"reach top_bound "<<top_bound<<" change direction from "<<direction<<" ";        
                    direction=(direction+1)%4;
                    top_bound++;
                    k++;
                    m++;
                    //cout<<"to "<<direction<<endl;
                    continue;
                }
                if(k==i&&m==j){
                    cout<<count<<endl;
                    return 0;
                }
                k--;
                count++;
                break;
        }
         
 
    }
    return 0;
}

2. 外圈优化法,o(n),可通过100%的数据。 

1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
假设想求得矩阵(2,3)位置上的值,可以从13-14-15-16这一内圈的开始点13作为锚点(anchor),模拟螺旋的方式计算出(2,3)的数。
13=4^2-2^2+1,即anchor= n*n-(inner_n*inner_n)+1,其中inner_n=n-2*dist,dist是内圈到外圈的距离;
anchor可以直接根据公式计算出,该算法主要的时间复杂度为模拟螺旋的时间,即数内圈的大小,o(inner_n)~o(n)
#include<iostream>
using namespace std;

/***************************
1. 1 ≤ n ≤ 30000,因此无法将整个矩阵存储下来,只能输出目标结果
2. 模拟整个过程仍会超时,可将情况分为3类
	(1)n==1, 输出1
	(2)i,j==1|n,即该数在旋转矩阵的外围计算旋转矩阵边界上的值
	(3)中间的数,根据首先计算外围数的个数,在外圈的数的基础上进行模拟 
****************************/
int n,i,j;
int anchor=1,dist,anchor_pos=1;

int min(int x,int y,int z,int q){
	return min(min(min(x,y),z),q);
}
int min(int x,int y){
	return x>y?y:x;
}

int main(){
	cin>>n>>i>>j;
	if(n==1){
		cout<<1<<endl;
	}else{
		int left,right,top,bottom;
		left=j-1;
		right=n-j;
		top=i-1;
		bottom=n-i;
		//cout<<left<<" "<<right<<" "<<top<<" "<<bottom<<endl;
		dist=min(left,right,top,bottom);
		anchor_pos=dist+1;
		anchor=n*n-(n-2*dist)*(n-2*dist)+1;
		//cout<<dist<<" "<<anchor<<endl;
		
		//接下来进行模拟,可在4*inner_n-4次计算中找到目标(i,j)
		int k=anchor_pos,m=anchor_pos;		//当前位置 
		int direction=0;					//当前方向 
		int top_bound=dist+1;			
		int bottom_bound=n-dist+1;
		int left_bound=dist;
		int right_bound=n-dist+1; 
		
		int count=anchor;
		
		while(count<=n*n){              
			switch(direction){
				case 0:
					//cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
					if(m==right_bound){
						//cout<<"reach right_bound "<<right_bound<<" change direction from "<<direction<<" ";
						direction=(direction+1)%4;
						right_bound--; 
						m--;
						k++;
						//cout<<"to "<<direction<<endl;
						continue; 
					} 
					if(k==i&&m==j){
						cout<<count<<endl;
						return 0;
					}
					m++;
					count++;
					break;
				
				case 1:
					//cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
					if(k==bottom_bound){
						//cout<<"reach bottom_bound "<<bottom_bound<<" change direction from "<<direction<<" ";
						direction=(direction+1)%4;
						bottom_bound--;
						k--;
						m--;
						//cout<<"to "<<direction<<endl;
						continue; 
					} 
					if(k==i&&m==j){
						cout<<count<<endl;
						return 0;
					}
					k++;
					count++;
					break;
					
				
				case 2:			
					//cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
					if(m==left_bound){
						//cout<<"reach left_bound "<<left_bound<<" change direction from "<<direction<<" ";
						direction=(direction+1)%4;
						left_bound++;
						m++;
						k--;
						//cout<<"to "<<direction<<endl;
						continue; 
					}
					if(k==i&&m==j){
						cout<<count<<endl;
						return 0;
					} 
					m--;
					count++;
					break;
				
				case 3:
					//cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
					if(k==top_bound){		
						//cout<<"reach top_bound "<<top_bound<<" change direction from "<<direction<<" ";			
						direction=(direction+1)%4;
						top_bound++;
						k++;
						m++;
						//cout<<"to "<<direction<<endl;
						continue; 
					} 
					if(k==i&&m==j){
						cout<<count<<endl;
						return 0;
					}
					k--;
					count++;
					break;
			}
		
		}
	} 
	return 0;
} 

3. 外圈+内圈优化,0(1)之路 

1 2 3 4
12 12+1 12+2 5
11 12+4 12+3 6
10 9 8 7

 
 
1 2
4 3
 
从图中可以看出内圈减去外圈的最大值后,变成了最简单的一圈。
外圈的最大值记为anchor=n^2-inner_n^2。
通过观察规律可以发现
如果(i,j)在内圈的上部或右部,(i,j)在内圈的位置为inner_i+inner_j-1
如果在内圈的左部或下部,(i,j)在内圈的位置为4*inner_n-inner_i-inner_j-1
(i,j)在整个矩阵中的位置为外圈的最大值+内圈的位置。
该算法使得结果可以由公式直接计算出,它的时间复杂度为常数o(1)。
 
#include<iostream>
using namespace std;

int n,i,j;
int anchor,dist;
int left,right,top,bottom;

int min(int x,int y,int z,int q){
	return min(min(min(x,y),z),q);
}

int min(int x,int y){
	return x>y?y:x;
}

int f(){
	int inner_n =n-2*dist; 
	int inner_i =i-dist;
	int inner_j =j-dist; 
	//cout<<inner_n<<" "<<inner_i<<" "<<inner_j<<" "<<endl; 
	if(i-1==dist||n-j==dist){ 
		return inner_i+inner_j-1; 
	}
	return 4*inner_n-inner_i-inner_j-1;
}

int main(){
	cin>>n>>i>>j;
	if(n==1){
		cout<<1<<endl;
	}else{
		int left,right,top,bottom;
		left=j-1;
		right=n-j;
		top=i-1;
		bottom=n-i;
		dist=min(left,right,top,bottom);
		anchor=n*n-(n-2*dist)*(n-2*dist);
		//cout<<dist<<" "<<anchor<<endl;
		int ans = anchor+f();
		cout<<ans<<endl;
	}
	return 0;
}

  

posted @ 2019-01-30 17:07  Bae  阅读(821)  评论(0编辑  收藏  举报