试题 算法训练 翻转旋转变换
资源限制
内存限制:256.0MB C/C++时间限制:1.0s Java时间限制:3.0s Python时间限制:5.0s
问题描述
现在有一张n行m列的由”o”和”*”字符组成的图案,需要你做若干次翻转和旋转操作,并输出最后的结果.
输入格式
第一行两个整数n和m
接下来n行,每行m个字符,表示待变换的图案
下一行一个整数p表示操作次数
接下来p行,每行一个整数,表示操作.(1代表水平翻转,2代表垂直翻转,3代表顺时针转90°,4代表逆时针转90°)
输出格式
输出n行m列(或m行n列)字符,表示变换后的图案
样例输入
4 5
**ooo
**o**
**oo*
**o**
2
2
3
样例输出
****
****
oooo
o*o*
o***
数据规模和约定
1<=n,m<=100
0<=p<=1000000
源码:
#include<stdio.h>
#include<algorithm>
using namespace std;
int main()
{
int i,j,k,n,m,p,x,g=0,change=0;
char t[4][102][102];
char *s[4][102];
char **q[4];
scanf("%d%d",&n,&m);
for(i=0;i<n;i++)
scanf("%s",&t[0][i]);
for(k=0;k<3;k++)
{
for(i=0;i<m;i++)
for(j=0;j<n;j++)
t[k+1][i][j]=t[k][n-j-1][i];
swap(m,n);
}
swap(m,n);
for(i=0;i<n;i++)
s[0][i]=t[0][i],s[2][i]=t[2][i];
for(i=0;i<m;i++)
s[1][i]=t[1][i],s[3][i]=t[3][i];
for(i=0;i<4;i++)
q[i]=s[i];
scanf("%d",&p);
while(p--)
{
scanf("%d",&x);
switch(x)
{
case 1:swap(q[g],q[(g+2)%4]);
for(i=0;i<n/2;i++)
swap(q[g][i],q[g][n-1-i]),swap(q[(g+2)%4][i],q[(g+2)%4][n-1-i]);
for(i=0;i<m/2;i++)
swap(q[(g+1)%4][i],q[(g+1)%4][m-1-i]),swap(q[(g+3)%4][i],q[(g+3)%4][m-1-i]);
break;
case 2:for(i=0;i<n/2;i++)
swap(q[g][i],q[g][n-1-i]),swap(q[(g+2)%4][i],q[(g+2)%4][n-1-i]);
swap(q[(g+1)%4],q[(g+3)%4]);
for(i=0;i<m/2;i++)
swap(q[(g+1)%4][i],q[(g+1)%4][m-1-i]),swap(q[(g+3)%4][i],q[(g+3)%4][m-1-i]);
break;
case 3:g=(g+1)%4;break;
case 4:g=(g+3)%4;break;
}
if(g%2!=change%2)
swap(n,m);
change=g;
}
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
printf("%c",q[g][i][j]);
printf("\n");
}
}
做题过程:
这题如果直接循环嵌套一个个交换的话,时间复杂度为1000000*100*100超过10亿会超时的。
之后我想到的是利用指针,在垂直反转时直接交换行指针便可,但是进行水平反转和顺逆旋转并不可以直接交换指针。
在然后我想到的是,这题循环次数特别多,而答案再怎么旋转反转答案也就那几种情况。是不是可以把所有答案都预先计算出来,再用一个变量来记录每一次循环变换后答案的位置。
大体解题思路:
如果我们只考虑顺逆时针操作的话,我们可以将旋转0度、90度、180度、270度的矩阵都计算出来,存放到一个三维数组里,再用一个下标来记录每次旋转后的位置,这样不用进行元素交换即可直接输出结果。在此基础上,我们只需要搞清楚在垂直反转和水平反转时,需要对三维数组进行什么操作即可。
过程分析:
定义char t[4][102][102]数组
录入原始数据存入t[0][102][102]中;
计算旋转90存入t[1][102][102]中;
计算旋转180存入t[2][102][102]中;
计算旋转270存入t[3][102][102]中;
scanf("%d%d",&n,&m);
for(i=0;i<n;i++)
scanf("%s",&t[0][i]);//录入原始数据
for(k=0;k<3;k++)
{
for(i=0;i<m;i++)
for(j=0;j<n;j++)
t[k+1][i][j]=t[k][n-j-1][i];//根据t[0]计算t[1],根据t[1]计算t[2]...
swap(m,n);//旋转后长宽互换
}
swap(m,n);//最后换回原来的m,n;
定义char *s[4][102] char **q[4];
让数组s指向数组t对应的行首,数组q指向s的行首,通过q来使用这个三维数组。交换数据时便可以直接交换指针,不需要一个个交换元素。
for(i=0;i<n;i++)//旋转0度和180度的行数为n
s[0][i]=t[0][i],s[2][i]=t[2][i];
for(i=0;i<m;i++)//旋转90度和270度的行数为m
s[1][i]=t[1][i],s[3][i]=t[3][i];
for(i=0;i<4;i++)
q[i]=s[i];
定义int g=0;来记录答案的位置从下标0开始。
定义int change=0;来记录上一次g的下标值;如果g下标变化不是二的倍数,则m和n的值需要互换。
当进行顺逆旋转操作时,只需要将g=(g+1)%4或g=(g+3)%4;
顺逆时针旋转90度后进行垂直反转和水平反转后进行顺逆时针旋转90度是等价的
所以,如果是垂直反转操作,需要对g和(g+2)%4的位置进行垂直反转操作,(g+1)%4和(g+3)%4位置进行水平反转操作;
垂直反转操作只需要上下行指针交换
水平反转操作将当前位置与加二%4的位置所有元素交换再进行垂直反转操作
scanf("%d",&p);
while(p--)
{
scanf("%d",&x);
switch(x)
{
case 1:swap(q[g],q[(g+2)%4]);//水平反转 先交换指针实现交换所有元素
for(i=0;i<n/2;i++)
swap(q[g][i],q[g][n-1-i]),swap(q[(g+2)%4][i],q[(g+2)%4][n-1-i]);//再垂直反转
for(i=0;i<m/2;i++)
swap(q[(g+1)%4][i],q[(g+1)%4][m-1-i]),swap(q[(g+3)%4][i],q[(g+3)%4][m-1-i]);//垂直反转
break;
case 2:for(i=0;i<n/2;i++)
swap(q[g][i],q[g][n-1-i]),swap(q[(g+2)%4][i],q[(g+2)%4][n-1-i]);//垂直反转
swap(q[(g+1)%4],q[(g+3)%4]);//水平反转 先交换指针实现交换所有元素
for(i=0;i<m/2;i++)
swap(q[(g+1)%4][i],q[(g+1)%4][m-1-i]),swap(q[(g+3)%4][i],q[(g+3)%4][m-1-i]);//再垂直反转
break;
case 3:g=(g+1)%4;break;
case 4:g=(g+3)%4;break;
}
if(g%2!=change%2)
swap(n,m);//如果变化g的变化不是二的倍数就行列交换
change=g;
}
总结:
二级指针的使用,通过交换指针来降低时间复杂度。
用取余实现循环。
对于答案种类少而一直循环运算的问题,看看是否可以预先算出答案,用下标记录答案的位置,简化运算。