PTA basic 1050 螺旋矩阵 (25 分) c++语言实现(g++)
本题要求将给定的 N 个正整数按非递增的顺序,填入“螺旋矩阵”。所谓“螺旋矩阵”,是指从左上角第 1 个格子开始,按顺时针螺旋方向填充。要求矩阵的规模为 m 行 n 列,满足条件:m×n等于 N;m≥n;且 m−n 取所有可能值中的最小值。
输入格式:
输入在第 1 行中给出一个正整数 N,第 2 行给出 N 个待填充的正整数。所有数字不超过 104,相邻数字以空格分隔。
输出格式:
输出螺旋矩阵。每行 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.为了形成螺旋矩阵的样式 需要的参数有 m行 n列 一个非增序排列的数组 给定的参数有元素个数N,待排序数列 ,所以第一步要把m和n计算得出,然后将数组有序(非增序)
2.题目已经给出了m,n的选择条件, 既 m-n 值最小 m>n m*n=N; N为正整数,使用双循环取m*n==N时的m和n值,由于m>n的条件,n循环取值时小于m即可
当m*n==N条件成立时, 取得m-n的值 和 min(初值为N)比较,如果m-n<min 则将m和n的值保存
3.螺旋数组的形成
螺旋数组有 四个方向 递增 的边,每个边的元素个数都是有特征的
手动推演 3*3 3*4 5*4 个元素的螺旋矩阵 按螺旋矩阵的螺旋边顺序 把每条边的元素都写出来之后 会发现一些规律
以5*5螺旋矩阵为例 数字表示元素在顺序数组中的位置,此时行m=5,列n=5;
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
第1条边 既 最上面一行 1 2 3 4 5 5个元素
第2条边 既 除第一行外的右侧边 6 7 8 9 4个元素
第3条边 既 除最右侧一列的底边 10 11 12 13 4个元素
第4条边 既 除最上最下两行的左侧边 14 15 16 3个元素
第5条边 递推 17 18 19 3个元素
第6条边 20 21 2个元素
第7条边 22 23 2个元素
第8条边 24 1个元素
第9条边 25 1个元素
观察可以得知,除第一条边外,每2条边的元素都会减1 因为每4条边方向是一圈 既 4条边会形成一个环
可以利用这个特征形成螺旋矩阵的坐标选择
4. 变量命名和含义
元素在数组中的坐标(row,col) 左行右列
round %4用于判断边的方向 0位左到右的横边 1为上到下的纵边 2位右到左的横边 3为下到上的纵边
maxColNums 初值 m 当前最大横边元素数量(列数)
maxRowNums 初值 n 当前最大纵边元素数量(行数)
count 当前横(纵)边已经放入的元素个数,当count==subSize的时候,需要换边 既 round++;
subSize 当前边的最大容量,初值maxColNums 既第一条横边的元素个数同时也是n
5.算法逻辑和边界调整
每条边都要经历一轮元素的放入,经过循环遍历 取值放入数组
遍历用的坐标 (raw,col) 在这条边的最后一个元素放入后,会执行一次+1操作,指向数组外
需要手动修正坐标位置,同时向下一条边移动
满足count==subSize时,移动到下一条边,round+1
同时对round进行判断,是要移动到哪条边,对应横(纵)边的元素最大容量 -1,然后赋值给subSize ,同时count清零
如果round%2==0;则下一条边是横边 横边元素最大容量 maxColNums-1
如果round%2==1;则下一条边是纵边 纵边元素最大容量 maxRowNums-1
round==1由 ➡️ 向 ⬇️ 移动时, 横坐标row+1 移动到下一行 ,纵坐标col-1回调到下一列
round==2由 ⬇️ 向 ⬅️ 移动时, 横坐标row-1 向上回调一行 ,纵坐标col-1移动到左边一列
round==3由 ⬅️ 向 ⬆️ 移动时, 横坐标row-1 移动到上一行 ,纵坐标col+1回调到右边一列
round==0由 ⬆️ 向 ➡️ 移动时, 横坐标row+1 向下回调一行 ,纵坐标col+1移动到右边一列
坐标经过处理后,会位于下一条边的第一个元素的位置
元素放入对应位置(row,col)后,要对坐标进行移动
round==0;说明是➡️的横边,col++;
round==1;说明是⬇️的纵边,row++;
round==2;说明是⬅️的横边,col--;
round==3;说明是⬆️的纵边,row--;
循环直到有序数组元素全部插入螺旋矩阵中
#include <iostream> #include <vector> #include <algorithm> using namespace std; int main(){ int N,temp,i,j,m{1},n{1},min,row{0},col{0},maxRowNums{0},maxColNums{0},subSzie{0},count{0},round{0}; cin >>N; vector<int> list; for(i=0;i<N;i++){ cin >> temp; list.push_back(temp); } sort(list.rbegin(),list.rend());//非增序排列 min=N; for(i=1;i<=N;i++){//求出m n for(j=1;j<=i;j++){ if((i*j)==N){ if((i-j)<min){ m=i; n=j; min=i-j; } } } } vector<vector<int>> rotateMetric(m,vector<int>(n));//m*n的矩阵,元素初值为0 maxColNums=n; maxRowNums=m; subSzie=maxColNums;//第一条横边的元素个数,是n; for(i=0;i<N;i++){ if(count==subSzie){//当前边元素已满,移动到下一条边 round++; if(round%2==0){//更新横(纵)边可容纳的最大个数 subSzie=--maxColNums; }else{ subSzie=--maxRowNums; } count=0;//清空元素计数count round=round%4;//计算下条边的方向 switch (round) {//坐标(row,col)移动到下条边的第一个元素的位置 case 0://由↑移动到→ row++; col++; break; case 1://由→移动到↓ row++; col--; break; case 2://由↓移动到← row--; col--; break; case 3://由←移动到↑ row--; col++; break; default: break; } } switch (round) { case 0://→的边,横坐标col右移 rotateMetric[row][col++]=list[i]; break; case 1://↓的边,纵坐标row下移 rotateMetric[row++][col]=list[i]; break; case 2://←的边,横坐标col左移 rotateMetric[row][col--]=list[i]; break; case 3://↑的边,纵坐标row上移 rotateMetric[row--][col]=list[i]; break; default: break; } count++;//每放入一个元素+1 } for(int i=0,j=0;i<m;i++){ for(j=0;j<n-1;j++){ printf("%d ",rotateMetric[i][j]); } printf("%d\n",rotateMetric[i][j]); } return 0; }