PAT乙组1050.螺旋矩阵思路与注意点--补充《算法笔记》

B1050

题目链接

个人思路

明显感觉复杂模拟题个人思路和代码思路还是不够好
首先拿到题目先想到要螺旋式写入矩阵然后输出
随后想到,能不能不写入矩阵,先找到每一行的输出规律,直接按行输出
想了半天决定看看答案思路再写
卡出上下左右四个边界,到达边界后更新边界,改变方向
参考过答案思路后

  1. 素数直接排序输出,否则找出合适的m,n
  2. cnt元素计数器作为while的循环条件
  3. dir表示当前填写方向
  4. 不同方向到达边界前正常处理,否则(从这一步开始,思路就不清晰了
    4.1.更新边界
    4.2判断是第一次到达边界,做相应处理,否则
    ****4.2.1.判断是完全平方数m == n,做相应处理,否则
    ****4.2.2.做相应处理
    4.3.换方向

个人思路代码

保证N = 39之前的输出正确,测试点有一个未通过
一开始还未注意到素数时,应该是m = N,n = 1

#include <bits/stdc++.h>
using namespace std;
int N;
int m, n;
vector<int> num;
int matrix[10010][10010];
int U, D, L, R;//上下左右四个边界 
void init()
{
	int sqr = (int)sqrt(1.0 * N);
	for(int i = sqr; i >= 1; --i)
	{
		if(N % i == 0)
		{
			m = i;
			break;
		}
	}
	n = N / m;
	if(n > m)
	{
		int temp = m;
		m = n;
		n = temp;
	}
	//行列均从下标为1开始 
	U = 2;
	D = m - 1;
	L = 2;
	R = n - 1;
}
bool isPrime(int x)
{
	if(x == 0 || x == 1)
		return false;
	int sqr = (int)sqrt(1.0 * x);
	for(int i = 2; i <= sqr; ++i)
	{
		if(x % i == 0)
		{
			return false;
		}
	}
	return true;
}
bool cmp(int a, int b)
{
	return a > b;
}
int main(int argc, char *argv[]) {
	scanf("%d", &N);
	for(int i = 0; i < N; ++i)
	{
		int a;
		scanf("%d", &a);
		num.push_back(a);
	}
	sort(num.begin(), num.end(), cmp);
	if(isPrime(N))//是素数 
	{
		for(int i = 0; i < num.size(); ++i)
		{
			printf("%d\n", num[i]);
		}
	}
	else//不是素数 
	{
		init();
		int cnt = 0, i = 1, j = 1;
		int dir = 3;//0:上,1:下,2:左,3:右
		bool isfirR = true, isfirD = true, isfirL = true; 
		while(cnt < N)
		{
			matrix[i][j] = num[cnt++];
			if(dir == 3)//先向右填
			{
				if(j < R)
				{
					j++;
				} 
				else//到达右边界  此时  j == R 
				{
					R--;
					if(isfirR)
					{
						j++;
						isfirR = false;
					}
					else
					{
						if(m == n)
							j++;
						else
							i++;
					}
					dir = 1;
				}
			}
			else if(dir == 1)//再向下填
			{
				if(i < D)
				{
					i++;
				}
				else//到达下边界 此时 j == D 
				{
					D--;
					if(isfirD)
					{
						i++;
						isfirD = false;
					}
					else
					{
						if(m == n)
							i++;
						else
							j--;
					}
					dir = 2;
				}
			}
			else if(dir == 2)//再向左填
			{
				if(j > L)
				{
					j--;
				}
				else//到达左边界 此时 j == L 
				{
					L++;
					if(isfirL)
					{
						j--;
						isfirL = false;
					}
					else
					{
						if(m == n)
							j--;
						else
							i--;
					}
					dir = 0;
				}
			}
			else if(dir == 0)//再向上填
			{
				if(i > U)
				{
					i--;
				}
				else//到达上边界 此时 i == U 
				{
					U++;
					dir = 3;
					j++;
				}
			}	
		}
		
		for(int x = 1; x <= m; ++x)
		{
			for(int y = 1; y <= n; ++y)
			{
				printf("%d", matrix[x][y]);
				if(y < n)
					printf(" ");
				else
					printf("\n");
			}
		}
	}
	return 0;
}

本题思路

有几个值得借鉴的地方

  • 特判N==1
  • 在代码过程中可以提前return 0
  • 循环体中 i++,j++返回内层左上角位置。这一步有点神

AC代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 10010;
int matrix[maxn][maxn],A[maxn];
bool cmp(int a, int b)
{
	return a > b;
}
int main(int argc, char *argv[]) {
	int N;
	scanf("%d", &N);
	for(int i = 0; i < N; ++i)
	{
		scanf("%d", &A[i]);
	}
	if(N == 1)
	{
		printf("%d\n", A[0]);
		return 0;
	}
	sort(A, A + N, cmp);
	//计算m,n
	int m = (int)ceil(sqrt(1.0 * N));
	while(N % m != 0)
	{
		m++;
	} 
	int n = N / m;
	int i = 1, j = 1, pos = 0;
	int U = 1, D = m, L = 1, R = n;
	while(pos < N)
	{
		while(pos < N && j < R)//向右填 
		{
			matrix[i][j] = A[pos++];
			j++;
		}
		while(pos < N && i < D)
		{
			matrix[i][j] = A[pos++];
			i++;
		}
		while(pos < N && j > L)
		{
			matrix[i][j] = A[pos++];
			j--;
		}
		while(pos < N && i > U)
		{
			matrix[i][j] = A[pos++];
			i--;
		}
		U++, D--, L++, R--;//更新边界 
		i++, j++;//位置移至内层左上角
		if(pos == N - 1)
		{
			matrix[i][j] = A[pos++];
		} 
	}
	for(int i = 1; i <= m; ++i)
	{
		for(int j = 1; j <= n; ++j)
		{
			printf("%d", matrix[i][j]);
			if(j < n)
				printf(" ");
			else
				printf("\n");
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值