题目链接:
http://poj.org/problem?id=3106
题目大意:
有一个m*n的矩阵,有10种操作,给一个操作串,求最后的矩阵。
操作'1' :沿主对角线翻转 行列交换
操作‘2’:沿副对角线翻转 行列交换
操作‘H':沿水平方向翻转
操作’V‘:沿竖直方向翻转
操作’A‘:顺时针旋转90度,’B':顺时针旋转180度,‘C'顺时针旋转270度。除180度行列不交换外,其他两种行列交换
操作’X‘:逆时针旋转90度,’Y‘:逆时针旋转180度,’Z‘逆时针旋转270度。除180度行列不交换外,其他两种行列交换
操作字符串最多有100000个,如果每一个操作都对矩阵的每个元素进行,肯定会超时。所以就会想到,如果把所有的操作都统一处理成简单的几种操作,最后等价处理后,再对矩阵进行一次操作,就会很简单,也不会超时。
关键是找等价关系,并且能够用数学语言等价描述。我的想法是,将十种操作都对应成H,V,和A三种操作,对每一个操作都转化成HVA的方式。
很显然有如下关系:H和V最多出现一个,因为H+V(先水平翻转再竖直翻转)=V+H(先竖直翻转再水平翻转)=A*2(顺时针旋转90度两次)。H+H=0(先水平翻转再水平翻转等于本身)同理:V+V=0 所以最后的结果集只可能是00a,01a,10a(a表示顺时针旋转90度的次数)
对于操作’1‘:等价于先逆时针旋转90度再H一次<=>先H再顺时针旋转90度一次。 XH=HA
对于操作’2‘:等价于先顺时针旋转90度再H一次<=>先H再逆时针旋转90度一次。AH=HX
对于操作’H‘:直接和前面的等价关系**a中的a(顺时针旋转90度的次数)组合成AH=HX,一直递推到V,a次顺时针全部转化为a次顺时针。
对于操作’V‘:XV=VA=AH=HX
对于’A‘,’B‘,’C‘直接加到顺时针次数上。
对于’X‘,’Y‘,’Z‘直接减到顺时针次数上。
所以用三个变量hh,vv,shun表示水平翻转次数,竖直翻转次数,顺时针旋转次数,注意是有顺序的。
XV=AH=HX AV=XH=HA
扫描每个操作,等价处理,不满足该顺序的借助等价关系改变顺序,保持该顺序不变。
详细解释代码:
#include<iostream>
#include<cmath>
#include<cstdio>
#include<sstream>
#include<cstdlib>
#include<string>
#include<string.h>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<ctime>
#include<bitset>
#define eps 1e-6
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define ll __int64
#define LL long long
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
#define M 1000000007
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
#define Maxn 330
#define M 110000
char a[Maxn][Maxn],b[Maxn][Maxn];
char ord[M];
int m,n;
void funh() //执行水平操作
{
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
b[m-i+1][j]=a[i][j];
memcpy(a,b,sizeof(b));
}
void funv() //执行竖直操作
{
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
b[i][n-j+1]=a[i][j];
memcpy(a,b,sizeof(b));
}
void funa() //执行顺时针旋转90度操作
{
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
b[j][m-i+1]=a[i][j];
swap(n,m);
memcpy(a,b,sizeof(b));
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
while(~scanf("%d%d",&m,&n))
{
for(int i=1;i<=m;i++)
scanf("%s",a[i]+1);
scanf("%s",ord);
int hh=0,ss=0; //hh表示水平翻转次数,ss表示竖直翻转次数
int shu=0,len=strlen(ord); //shu表示顺时针旋转次数
int flag=0;
for(int i=0;i<len;i++)
{
switch(ord[i])
{
case '1':
{
flag^=1; //行列交换
//由 XH=HA 和 AH=HX
if(shu) //如果之前有顺时针旋转
shu=(-shu+4)%4; //顺时针全部转化为逆时针
shu=(shu+1)%4; //最后还有个A
if(hh) //H+H=0
hh=0;
else if(ss) //V+H=AA
{
hh=0;
ss=0;
shu=(shu+2)%4;
}
else //H=0 V=0时H=1
hh++;
}
break;
case '2':
{
flag^=1; //HX
if(shu)
shu=(-shu+4)%4;
shu=(shu-1+4)%4; //最后有一个X
if(hh)
hh=0;
else if(ss)
{
hh=0;
ss=0;
shu=(shu+2)%4;
}
else
hh++;
}
break;
case 'V':
{
if(shu) //AV=XH=HA
{
shu=(-(shu-1)+4)%4;
shu=(shu+1)%4; //最后有一个A
if(ss)
{
ss=0;
hh=0;
shu=(shu+2)%4;
}
else if(hh)
hh=0;
else
hh++;
break;
}
if(ss)
{
ss=0;
hh=0;
//shu=(shu+2)%4;
}
else if(hh)
{
ss=0;
hh=0;
shu=(shu+2)%4;
}
else
ss++; //如果一个H和V都没有,ss++
}
break;
case 'H':
{
if(shu)
shu=(4-shu)%4;
if(ss)
{
ss=0;
hh=0;
shu=(shu+2)%4;
}
else if(hh)
hh=0;
else
hh++;
}
break;
case 'A':
shu=(shu+1)%4;flag^=1;break;
case 'B':
shu=(shu+2)%4;break;
case 'C':
flag^=1;shu=(shu+3)%4;break;
case 'X':
flag^=1;shu=(shu-1+4)%4;break;
case 'Y':
shu=(shu-2+4)%4;break;
case 'Z':
flag^=1;shu=(shu-3+4)%4;break;
}
}
//printf("flag:%d hh:%d ss:%d shu:%d\n",flag,hh,ss,shu);
if(hh) //注意执行顺序 H V a
funh();
else if(ss)
funv();
if(shu) //顺时针旋转次数
{
while(shu--)
funa();
}
/*if(flag)
swap(n,m);*/
printf("%d %d\n",m,n);
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
printf("%c",a[i][j]);
putchar('\n');
}
}
return 0;
}