P1205 [USACO1.2] 方块转换 Transformations 详细题解 原理解释+代码注释

蒟蒻第一次写题解,求大佬勿喷,完整代码请扒拉到最底下。

解题要求:只需要会基础的判断,循环,数组,指针,函数即可

首先是找规律

题目中很明显需要二维数组来存储矩阵,然后要求我们进行坐标变换,一一对应比较,最终判断是哪种情况,但是光是这个坐标变换的数学规律就需要思考挺久,愣是在纸上算了半天没算出来,但是我岂能被区区困难打倒,于是我选择了放弃参考大佬,得到了各种情况坐标变换的规律。

其次是顺利输入、读取和存储矩阵元素

说实话这个对于我这个菜鸡来说也是挺麻烦的一件事

首先用char定义两个二维数组,分别存储两个矩阵。

char mtx[11][11];//原始矩阵
char tmtx[11][11];//变换后的矩阵

常见问题:

为啥不用int、string?

因为存储的元素单位是字符。

输入是连续的字符/输入里边有回车该怎么输入?

用两个for循环逐个输入

for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			cin >> mtx[i][j];
		}
	}

输入第一个字符后随着循环继续进行,cin会自动进入下一个等待输入,至于回车或者空格,甚至只用一行全部输入完全不会影响数据存入矩阵的顺序,因为cin是从输入流中读取数据,对于字符串输入,cin默认使用空白字符(空格、制表符和回车等)作为分隔符,这意味着cin在遇到空格或回车时会将输入截断,并将你输入的内容存入数组。

完成这些之后,你可以自己尝试输出一下两个矩阵,检查自己是否正确输入和存储,这里我就不写了,把上边的cin改成cout就OK了。

接下来就是将坐标变换规律用代码表示出来

这里就需要用到函数和指针的知识了。

使用函数可以让我们的代码更加简洁,更加模块化,因为重复的部分只需要写一小行调用就完了,纠错修改也只需要修改一处。

使用指针是因为我们需要修改传入函数的值,所以需要用到指针传入地址。

以旋转90度举个栗子:

void turn90(int *i,int *j)//顺时针旋转90度
{
	int vessel;
	vessel = *j;
	*j = n - *i + 1;
	*i = vessel;
}

turn90(&k, &l);//调用函数

然后来到第二个关键部分:如何判断

这里我们需要判断的是,转换后的矩阵的矩阵和题目要求的矩阵是以何种方法转换的?

这里我为每一种情况建立了一个标号,标号的数字就代表相应的情况,也就是我最终输出的东西:

int seq[9] = {1,2,3,4,5,5,5,6,7};
//其中5是三种情况的组合,只要满足一个组合输出的都是5			 
//如果只定义一个5的话,那么一旦其中一个情况被否定,更改标号之后,剩下另外两个情况就无法继续讨论
//所以组合的三种情况都各自对应一个5,这样既可以分开讨论,输出的时候也保证都是5

那我是如何具体实现标记和判断功能的呢?还是拿旋转90度举个栗子:

for (int i = 1; i <= n; i++)//遍历矩阵
	{
		for (int j = 1; j <= n; j++)
		{
			if (seq[0] == 1)
			//首先对标号进行判断,若标号未被更改,则进入下边的代码
			//否则直接终止剩下坐标转换,这样就不需要把所有坐标转换完再进行比较了
			
			{
				k = i;
				l = j;
				turn90(&k, &l);//调用函数,传入地址更改实参,转换坐标
				if (tmtx[k][l] != mtx[i][j])//如果发现和转换后的矩阵元素对不上
				{
					seq[0] = 10;//则更改标号,之后就不需要再次进行坐标变换了
                    			//否则就继续逐个对比	
                    
				}
			}
		}
	}

所以这样每种情况都试一遍的情况下,标号可能会变这样:

int seq[9] = {10,10,10,10,5,10,10,6,7};

此时5、6代表着可以通过镜像翻转达到,同时转换后的图像和原图像一样,剩下的10代表着该方法被否决了,此时我们利用sort函数进行一次升序排序:

int seq[9] = {5,6,7,10,10,10,10,10,10};

那么第一个元素就是5,我们只需要输出第一个元素,就能达到题目“如果有多种可用的转换方法,请选择序号最小的那个”的要求。

最后一个是7(代表无法用以上方法得到新图案)的原因是:

如果前边有方法满足条件,那么这个数会小于7,7会排在它后边不会输出。

如果都没有条件满足,那么其他数都是10,都会大于7,这样7就会排在第一个被输出了,这样就减少了一次判断。

完整代码

#include<iostream>	
#include<algorithm>
using namespace std;
char mtx[11][11];//原始矩阵
char tmtx[11][11];//变换后的矩阵

int seq[9] = {1,2,3,4,5,5,5,6,7};
//存储每种情况的标号,也是最后要输出的东西
//如果对比过程中发现此情况不满足,就更改标号为10(大于7就行)
//通过sort函数升序排序,将标号为10的元素挪到后边,将最小的元素挪到最前边
//然后输出第一个元素seq[0]
//即满足了题目的要求:“如果有多种可用的转换方法,请选择序号最小的那个”

//其中5是三种情况的组合,只要满足一个组合输出的都是5			 
//如果只定义一个5的话,那么一旦其中一个情况被否定,更改标号之后,剩下另外两个情况就无法继续讨论
//所以组合的三种情况都各自对应一个5,这样既可以分开讨论,输出的时候也保证都是5

//最后一个是7(代表无法用以上方法得到新图案)的原因是,
//如果前边有方法满足条件,那么这个数会小于7,7会排在它后边不会输出
//如果都没有条件满足,那么其他数都是10,都会大于7,这样7就会排在第一个被输出了
//这样就减少了一次判断
int n;

void turn90(int *i,int *j)//顺时针翻转90度
{
	int vessel;
	vessel = *j;
	*j = n - *i + 1;//坐标变换公式,参考的大佬的呜呜呜
	*i = vessel;
}

void turn180(int* i, int* j)//顺时针翻转180度
{
	int vessel1;
	vessel1 = *j;
	*j = n - *i + 1;
	*i = vessel1;

	int vessel2;
	vessel2 = *j;
	*j = n - *i + 1;
	*i = vessel2;//执行两次翻转90度就是翻转180度
}

void turn270(int* i, int* j)//顺时针翻转270度
{
	int vessel1;
	vessel1 = *j;
	*j = n - *i + 1;
	*i = vessel1;

	int vessel2;
	vessel2 = *j;
	*j = n - *i + 1;
	*i = vessel2;

	int vessel3;
	vessel3 = *j;
	*j = n - *i + 1;
	*i = vessel3;//执行三次翻转90度就是翻转270度
}

void mirror(int* i, int* j)//水平方向沿铅垂线翻转
{
	*j = n - *j + 1;//还是参考大佬的公式
}

void combine1(int* i, int* j)//组合1
{
	*j = n - *j + 1;//镜像翻转
	
	int vessel;
	vessel = *j;
	*j = n - *i + 1;
	*i = vessel;//旋转90度
}

void combine2(int* i, int* j)//组合2
{
	*j = n - *j + 1;//镜像翻转

	int vessel1;
	vessel1 = *j;
	*j = n - *i + 1;
	*i = vessel1;

	int vessel2;
	vessel2 = *j;
	*j = n - *i + 1;
	*i = vessel2;//旋转180度
}

void combine3(int* i, int* j)//组合3
{
	*j = n - *j + 1;//镜像翻转

	int vessel1;
	vessel1 = *j;
	*j = n - *i + 1;
	*i = vessel1;

	int vessel2;
	vessel2 = *j;
	*j = n - *i + 1;
	*i = vessel2;

	int vessel3;
	vessel3 = *j;
	*j = n - *i + 1;
	*i = vessel3;//旋转270度
}

int main()
{
	int k = 0, l = 0;//用于暂存i,j,这样就不会更改i,j的值

	cin >> n;

	for (int i = 1; i <= n; i++)//存入原始矩阵,注意是从1开始的,这样才能对应公式
								//如果是从0开始则公式也要做相应修改,之前就在这里纠结半天
	{
		for (int j = 1; j <= n; j++)
		{
			cin >> mtx[i][j];
		}
	}

	for (int i = 1; i <= n; i++)//存入改变后的矩阵,也是从1开始
	{
		for (int j = 1; j <= n; j++)
		{
			cin >> tmtx[i][j];
		}
	}

	for (int i = 1; i <= n; i++)//开始转换原始矩阵,在转换的同时和第二个矩阵逐个比较,记得从1开始!!
	{
		for (int j = 1; j <= n; j++)
		{
			if (seq[0] == 1)//旋转90度
			//对标号进行判断,若标号未被更改,则继续转换
			//否则直接终止剩下坐标转换,就不需要把所有坐标转换完再进行比较了
			
			{
				k = i;
				l = j;
				turn90(&k, &l);//调用函数,传入地址更改实参,转换坐标
				if (tmtx[k][l] != mtx[i][j])//如果发现和转换后的矩阵元素对不上
				{
					seq[0] = 10;//则更改标号,之后就不需要再次进行坐标变换了
				}
			}

			if (seq[1] == 2)//旋转180度
			{
				k = i;
				l = j;
				turn180(&k, &l);
				if (tmtx[k][l] != mtx[i][j])
				{
					seq[1] = 10;
				}
			}
			
			
			if (seq[2] == 3)//旋转270度
			{
				k = i;
				l = j;
				turn270(&k, &l);
				if (tmtx[k][l] != mtx[i][j])
				{
					seq[2] = 10;
				}
			}
			
			if (seq[3] == 4)//镜像旋转
			{
				k = i;
				l = j;
				mirror(&k, &l);
				if (tmtx[k][l] != mtx[i][j])
				{
					seq[3] = 10;
				}
			}
			
			if (seq[4]  == 5)//组合1
			{
				k = i;
				l = j;
				combine1(&k, &l);
				if (tmtx[k][l] != mtx[i][j])
				{
					seq[4] = 10;
				}
			}
			
			if (seq[5] == 5)//组合2
			{
				k = i;
				l = j;
				combine2(&k, &l);
				if (tmtx[k][l] != mtx[i][j])
				{
					seq[5] = 10;
				}
			}
			
			if (seq[6] == 5)//组合3
			{
				k = i;
				l = j;
				combine3(&k, &l);
				if (tmtx[k][l] != mtx[i][j])
				{
					seq[6] = 10;
				}
			}
			if (seq[7] == 6)//一毛一样
			{
				if (tmtx[i][j] != mtx[i][j])
				{
					seq[7] = 10;
				}
			}

		}
	}

	sort(seq, seq + 9);//利用sort函数升序排序,选出数组中最小的数,
					   
	cout << seq[0];//最终输出最小的结果

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值