题目
本题要求将给定的 N 个正整数按非递增的顺序,填入“螺旋矩阵”。所谓“螺旋矩阵”,是指从左上角第 1 个格子开始,按顺时针螺旋方向填充。要求矩阵的规模为 m 行 n 列,满足条件:m×n 等于 N;m≥n;且 m−n 取所有可能值中的最小值。
输入格式:
输入在第 1 行中给出一个正整数 N,第 2 行给出 N 个待填充的正整数。所有数字不超过 10的4次方,相邻数字以空格分隔。
输出格式:
输出螺旋矩阵。每行 n 个数字,共 m 行。相邻数字以 1 个空格分隔,行末不得有多余空格。
输入样例:
12
37 76 20 98 76 42 53 95 60 81 58 93
输出样例:
98 95 93
42 37 81
53 20 76
58 60 76
思考过程
此题开始以为又是什么数学规律或者矩阵翻转的题,其实就是简单模拟类型。
1.一开始就在研究这种顺时针在矩阵内遍历会有什么规律,经过几个极端用例对比,发现一个规律,都只需要循环行数+列数-1的次数。
2.我选择了双循环,外面是循环次数,里面是有四个不同情况下的循环,分别是按顺时针来:左右,上下,右左,下上,这样来。毕竟就按照简单模拟来
3.测试点,最后一个测试点也是个小陷阱,当初我初始化矩阵为output[100][100],这是错误的,会导致段错误,因为少考虑了一个极端例子,像行数为99997,同时列数为1的情况,修改后也顺利通过
4.当初在想给出一个N,怎么使其分解的乘积能比较近,这个得分情况,先开根号,然后判断是否整除,不能被整除就逆序逐个寻找能够整除为止。
5.这次为了贪方便,没直接自己写排序代码,就直接用了STL的sort。
代码
#include <stdio.h>
#include <algorithm>
#include <math.h>
#define MAX_NUM 10001
/*
1050 螺旋矩阵 (25 分)
*/
using namespace std;
int GetIntputToInt();
bool cmp(int a,int b);
int output[10000][100]={0};//output[100][100],这是我一开始写成这样,导致最后一个测试点过不了,考虑到类似9997行,1列这种情况
int main()
{
int N,rownum,colnum,arr[MAX_NUM];
//1.输入
N=GetIntputToInt();
for(int i=0;i<N;i++)
arr[i] = GetIntputToInt();
colnum = sqrt(N);
if(N%colnum==0)
{
if(N==colnum*colnum)
rownum = colnum;
else
rownum = N/colnum;
}
else
{
colnum--;
while(N%colnum!=0)
colnum--;
rownum = N/colnum;
}
//2.处理,在草稿纸上发现了一个规律,按照螺旋矩阵的规律,例如4*3,需要循环4+3-1次,其他也是这样
sort(arr,arr+N,cmp);
//isrow当前是不是遍历行,否则就是遍历列。isupdown是遍历从上到下,否则就是下到上。isleftright是遍历从左到右,否则就是从右到左。
int isrow=1,isupdown=1,isleftright=1,times = rownum+colnum-1;
for(int i = 0,j=0,k=0,cur=0;i<times;i++)
{
//遍历顺序是左往右,上往下,右往左,下往上 顺序遍历
//j控制行,k控制列
if(isrow)
{//行遍历
if(isleftright)//是否左右
{
//正在第j行,遍历列
for(;output[j][k]==0&&k<colnum;k++)
output[j][k] = arr[cur++];
//为下一次的上下遍历准备
j++;//下一行
k--;//退回前一列,因为刚刚超出边界了
isleftright = 0;
}
else
{
for(;k>=0&&output[j][k]==0;k--)
output[j][k] = arr[cur++];
//为下一次的下上遍历准备
j--;//上一行
k++;// 退回前一个
isleftright = 1;
}
isrow = 0;
}
else
{//列
if(isupdown)//是否上下
{
//正在第k列,遍历行
for(;output[j][k]==0&&j<rownum;j++)
output[j][k] = arr[cur++];
//为下一次的右左遍历准备
j--;//退回上一行
k--;// 退回前一列
isupdown=0;
}
else
{
//正在第k列,遍历行 (这里j>0,是因为顺时针回来的话,这里的下上一定至少是第二行,也就是j=1)
for(;output[j][k]==0&&j>0;j--)
output[j][k] = arr[cur++];
//为下一次的左右遍历准备
j++;//退回上一行
k++;// 退回前一列
isupdown=1;
}
isrow = 1;
}
}
//3.输出
for(int i = 0,j=0;i<rownum;i++,j=0)
{
for(;j<colnum-1;j++)
printf("%d ",output[i][j]);
printf("%d\n",output[i][j]);
}
return 0;
}
bool cmp(int a,int b)
{
return a>b;
}
int GetIntputToInt()
{
char c;
int sum=0;
while((c=getchar())!=EOF&&c!=' '&&c!='\n')
sum=sum*10+c-'0';
return sum;
}