信息学奥赛一本通 1967:【14NOIP普及组】螺旋矩阵 | 洛谷 P2239 [NOIP2014 普及组] 螺旋矩阵

【题目链接】

ybt 1967:【14NOIP普及组】螺旋矩阵
洛谷 P2239 [NOIP2014 普及组] 螺旋矩阵

类似考题:
洛谷 P1014 [NOIP1999 普及组] Cantor 表

【题目考点】

1.二维数组

【解题思路】

记输入的目标位置为(ai, aj)
如果暴力移动焦点,在二位数组中蛇形填写数字,因为数组边长达到30000,数组元素个数为 30000 ∗ 30000 = 9 ∗ 1 0 8 30000*30000=9*10^8 3000030000=9108,默认计算机1s可以进行小于 1 0 8 10^8 108次运算,这样做一定会超时。
每圈的元素个数是容易求的,如果每次移动一圈,到(ai,aj)所在的圈时再一个一个格子移动,这样移动次数就会大大减少。
第1圈边长为n,元素个数为4*(n-1)
第2圈边长为n-2,元素个数为4*(n-2-1)

第i圈边长为n-2*(i-1),元素个数为4*(n-2*(i-1)-1)
每圈边长比上一圈少2,元素个数为4*(边长-1)

第i圈:上面一行的行坐标为:i,下面一行行坐标为:n-i+1,左侧一列列坐标为:i,右侧一列列坐标为n-i+1。
如果目标位置:(ai, aj)的行坐标ai为i或n-i+1,或列坐标aj为i或n-i+1,说明(ai,aj)在第i圈。然后可以有两种方法确定(ai, aj)位置的值。

解法1:移动焦点

第i圈边长为l=n-2*(i-1),将一圈分为等长的四部分,每部分长度为:l-1。焦点从左上角出发,向右移动l-1次后,焦点指向右上角;再向下移动l-1次后,焦点指向右下角;再向左移动l-1次,焦点指向左下角;再向上移动l-1次,完成对第i圈的遍历。
在这一过程中,记录到每个位置时数组元素值。如果遍历到(ai, aj),输出该值。

解法2:坐标计算

记第i圈左上角位置(i,i)的值为st,边长为l。那么右上角(i,n-i+1)的值为:st+l-1,右下角(n-i+1,n-i+1)的值:st+(l-1)*2,左下角(n-i+1,i)的值:st+(l-1)*3。

  • 如果目标位置在第i行,需要从左上角(i,i)移动到(ai,aj)位置,移动aj-i次,值需要加aj-i,那么(ai,aj)位置的值为st+aj-i
  • 如果目标位置在第n-i+1列,需要从右上角(i,n-i+1)移动到(ai,aj)位置,移动ai-i次,那么(ai,aj)位置的值为st+(l-1)+ai-i
  • 如果目标位置在第n-i+1行,需要从右下角(n-i+1,n-i+1)移动到(ai,aj)位置,移动n-i+1-aj次,那么(ai,aj)位置的值为st+2*(l-1)+(n+1-i)-aj
  • 如果目标位置在第i列,需要从左下角(n-i+1,i)移动到(ai,aj)位置,移动n-i+1-ai次,那么(ai,aj)位置的值为st+3*(l-1)+(n+1-i)-ai

【题解代码】

解法1:移动焦点
#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n, ai, aj, i, st, l;//求(ai,aj)的数 st:该圈起始数字 
    int dir[4][2] = {{0,1},{1,0},{0,-1},{-1,0}};//右下左上 
    cin >> n >> ai >> aj;
    st = 1, l = n;//st:起始数字, l:该圈边长 
    for(i = 1; i <= n/2; ++i)
    {//看ai,aj是否在第i圈 
        if(ai == i || ai == n+1-i || aj == i || aj == n+1-i)
            break;
        st += 4*l-4;
        l -= 2;        
    }
    //看ai,aj位置是第i圈上第几个格子,求出目标位置的值 
    int fi = i, fj = i, num = st;//fi,fj:焦点 num:该位置的值  
    for(int d = 0; d < 4; ++d)//方向 0:右 1:下 2:左 3:上 
    {
    	int k = 1;
    	do//用do...while是为了当l==1时,也会判断输出解。解决找边长为奇数的二维数组中心位置的问题。 
    	{
    		if(fi == ai && fj == aj)//找到目标位置 
    		{
    			cout << num;
    			return 0;
			}
			fi += dir[d][0], fj += dir[d][1];//按照d方向移动到下一个位置 
			num++;
			k++;
		}while(k <= l-1);//每一条边遍历l-1次 
	}
    return 0;
}
解法2:坐标计算
#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n, ai, aj, i, st, l;//求(ai,aj)的数 st:该圈起始数字 
    cin >> n >> ai >> aj;
    st = 1, l = n;//st:起始数字, l:该圈边长 
    for(i = 1; i <= n/2; ++i)
    {//看ai,aj是否在第i层外圈 
    	if(ai == i || ai == n+1-i || aj == i || aj == n+1-i)
            break;
        st += 4*l-4;
        l -= 2;        
    }
    if(ai == i)//在第i行
		cout << st+aj-i;//移动aj-i次
	else if(aj == n+1-i)//在第n-i+1列
		cout << st+l-1+ai-i;//移动ai-i次
	else if(ai == n+1-i)//在第n-i+1行
		cout << st+2*(l-1)+(n+1-i)-aj;//移动(n+1-i)-aj次
	else if(aj == i)//在第i列
		cout << st+3*(l-1)+(n+1-i)-ai;//移动(n+1-i)-ai次
    return 0;
}
解法3:递归
#include<bits/stdc++.h>
using namespace std;
int getNum(int l, int i, int j)//左上角(1,1)位置的值为1,边长为l的矩阵中(i,j)的值
{
	if(i == 1)//如果在上边 
		return j;
	else if(j == l)//如果在右边 
		return l-1+i;
	else if(i == l)//如果在下边 
		return 2*(l-1)+l-j+1;
	else if(j == 1)//如果在左边 
		return 3*(l-1)+l-i+1;
	else//如果在内部 i,j位置的值为这一圈元素的个数(4*(l-1))加上以(2,2)位置为左上角,以l-2长度为边的矩阵中,(i-1,j-1)位置的值。 
		return 4*(l-1)+getNum(l-2, i-1, j-1);//以(2,2)位置为左上角时, 原(2,2)变为(1,1),原(i,j)变为(i-1,j-1) 
}
int main()
{
    int n, ai, aj;//求(ai,aj)的数 st:该圈起始数字 
    cin >> n >> ai >> aj;
    cout << getNum(n, ai, aj);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值