来临挑战1:帮助圣诞老人解开他现在的保险库!


18

下一个>>

描述性关键字(用于搜索):使两个矩阵等效,重叠,数组,查找

挑战

圣诞老人在过去曾有过精灵从他的保险库中偷走礼物的历史,因此,今年他设计了一把很难破解的锁,今年似乎将精灵拒之门外。不幸的是,他丢失了组合,也无法弄清楚如何打开它!幸运的是,他雇用了您编写一个程序来查找该组合。它不需要是最短的,但他需要尽快找到它!

他的日程安排非常严格,他等不起。您的分数将是程序的总运行时间乘以程序为得分输入所输出的步数。最低分获胜。

技术指标

锁是1和0的方阵。它设置为1和0的随机排列,并且需要设置为指定的代码。幸运的是,圣诞老人记住了所需的代码。

他可以执行几个步骤。每个步骤都可以在任何连续的子矩阵上执行(也就是说,您必须选择一个完全由左上角和右下角所包围的子矩阵)(可以是非正方形子矩阵):

  1. 向右旋转90度*
  2. 向左旋转90度*
  3. 旋转180度
  4. 左右循环各行n元素(环绕)
  5. 循环上移m或下移每个列元素(自动换行)
  6. 水平翻转
  7. 垂直翻转
  8. 翻转主对角线*
  9. 翻转主反对角线*

*仅当子矩阵为正方形时

当然,他也可以在整个矩阵上执行这些步骤。由于1和0只能在矩阵上交换,而正方形的值不能直接更改,因此开始和结束配置的1和0的数目相同。

格式规范和规则

将以您想要的任何合理格式为您提供两个平方矩阵的输入(开始位置和结束位置)。输出应该是任何可读格式的这些步骤的序列。由于这不是代码来源,请使其成为易于验证的格式,但这不是严格的要求。如果需要,可以选择在输入中采用矩阵的边长。

您的程序将在我的计算机上运行(Linux Mint,如果需要的话,您可以根据要求提供确切的版本详细信息:P),并且我将根据我在命令行上按“ Enter”键之间的时间以及命令退出。

测试用例

1 0 0 1    0 0 0 0
0 1 1 0 -> 0 0 0 0
0 1 1 0 -> 1 1 1 1
1 0 0 1    1 1 1 1
  1. 取整个矩阵。循环上每一列1。
  2. 将中间两列作为子矩阵。将每列向下循环2。
1 0 1 0 1    0 1 0 1 0
0 1 0 1 0    1 0 1 0 1
1 0 1 0 1 -> 0 1 1 1 0
0 1 0 1 0    1 0 1 0 1
1 0 1 0 1    0 1 0 1 0
  1. 取整个矩阵。将每列向下循环1。
  2. 取中间一列。循环下来2。
  3. 采取前2行。垂直翻转。
  4. 采取最上面一行的最右边的2个元素。交换它们(左右旋转1,水平翻转)。
  5. 取第一行最左边的2个元素。交换他们。

可能有更有效的方法,但这无关紧要。如果发现有问题,请随时在评论中指出:)

判断测试用例

该测试用例将用于判断您的提交。如果我认为答案过多地适用于测试用例,那么我有权取消随机输入,并用新案例重新判断所有答案。在这里可以找到测试用例其中顶部是起点,底部是所需的配置。

如果我认为答案过于专业,那么下一个测试用例的MD5为3c1007ebd4ea7f0a2a1f0254af204eed。(这是现在写在这里,以使自己摆脱指控作弊:P)

适用标准漏洞。没有答案将被接受。编码愉快!

注意:我从Advent Of Code汲取灵感来挑战这个系列。我没有与此网站的隶属关系

通过在此处查看第一个挑战的“链接”部分,可以查看该系列中所有挑战的列表。


信息:测试用例有192 0和64 1,并且总共有256 choose 64 ≈ 1.9 × 10⁶¹可及的矩阵。(与Megaminx相当,并且比Rubik's Revenge大,尽管比Professor's cube
小得多

Answers:


1

爪哇

import java.util.Arrays;

public class SantaMatrix4 {
	
	public static void flipV(int[][] matrix, int row1, int col1, int row2, int col2) {
		for (int row = row1; row <= (row2 - row1) / 2 + row1; row++) {
			for (int col = col1; col <= col2; col++) {
				int tmp = matrix[row][col];
				matrix[row][col] = matrix[row2 - row + row1][col];
				matrix[row2 - row + row1][col] = tmp;
			}
		}
	}
	
	public static void flipH(int[][] matrix, int row1, int col1, int row2, int col2) {
		for (int row = row1; row <= row2; row++) {
			for (int col = col1; col <= (col2 - col1) / 2 + col1; col++) {
				int tmp = matrix[row][col];
				matrix[row][col] = matrix[row][col2 - col + col1];
				matrix[row][col2 - col + col1] = tmp;
			}
		}
	}

	public static void main(String[] args) {
		int counter = 0;
		int n = Integer.parseInt(args[counter++]);
		int[][] matrix1 = new int[n][n];
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				matrix1[i][j] = Integer.parseInt(args[counter++]);
			}
		}
				
		int[][] matrix2 = new int[n][n];
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				matrix2[i][j] = Integer.parseInt(args[counter++]);
			}
		}
			
		int[] ops = new int[5 * matrix1.length * matrix1.length * 2];
		int numOps = 0;
		int opsI = 0;
		
		for (int row = 0; row < n; row++) {
			for (int col = 0; col < n; col++) {
				int goal = matrix2[row][col];
				boolean gotIt = false;
				
				//Look for required number to the right
				for (int i = row; i < n && !gotIt; i++) {
					for (int j = col; j < n && !gotIt; j++) {
						if (i == row && j == col) continue;
						if (matrix1[i][j] == goal) {
							flipH(matrix1, row, col, i, j);
							flipV(matrix1, row, col, i, j);
							ops[opsI++] = 1;
							ops[opsI++] = row;
							ops[opsI++] = col;
							ops[opsI++] = i;
							ops[opsI++] = j;
							numOps++;
							
							gotIt = true;
						}
					}
				}

				//Look for required number below and to the left
				for (int i = row + 1; i < n && !gotIt; i++) {
					for (int j = 0; j < col && !gotIt; j++) {
						if (matrix1[i][j] == goal) {
							flipH(matrix1, i, j, i, col);
							ops[opsI++] = 2;
							ops[opsI++] = i;
							ops[opsI++] = j;
							ops[opsI++] = i;
							ops[opsI++] = col;
							
							flipV(matrix1, row, col, i, col);
							ops[opsI++] = 3;
							ops[opsI++] = row;
							ops[opsI++] = col;
							ops[opsI++] = i;
							ops[opsI++] = col;
							
							numOps += 2;
							gotIt = true;
						}
					}
				}
				
			}
		}

		System.out.println(Arrays.toString(ops));
		System.out.println(numOps);
	}
}

硬编码版本稍快:在线试用!

通过命令行输入是空格分隔的整数。第一个整数是两个矩阵的宽度。其余整数是它们的元素,逐行。

矩阵的每个排列都可以仅通过水平翻转和垂直翻转运算符获得,因此我忽略了其余部分,只是用180度旋转替换了同一区域中的连续vFlip和hFlip。

程序扫描每个元素。每当我们遇到具有错误位的元素时,它就会在数组中向前看,以找到具有正确位的点。我将搜索区域分为两部分:一列具有相等或更大的坐标,一列具有较小的坐标。请注意,根据我们遍历数组的方式,后者必须具有较大的行坐标。如果我们在第一个搜索区域中找到正确的位,就可以将跨越两个元素的子矩阵旋转180度,总共进行一次操作。如果它在第二个区域中,我们可以使用水平翻转将正确的位与错误的位移至同一列,然后垂直翻转跨越这两个子矩阵的子矩阵,总共进行两次操作。

程序的输出是一个数组,应该在精神上分成五个一组。每个组是(i,row1,col1,row2,col2),其中,对于无操作,i为0,对于180度旋转为1,对于水平翻转为2,对于垂直翻转为3。其余4个组件描述了运行该操作的区域。我不确定这是否是可读格式。

对于给定的测试用例,我的计算机上进行了258次操作和2到3毫秒。


@Erik the Outgolfer未指定,并且对其进行硬编码使其更易于判断。
WhatToDo

我已将其更改为从命令行接受输入
WhatToDo

此输出格式足够合理。我虽然在数组中获得了1000个数字(200个操作?),所以258的来源是什么?对于如何读取此输出,我有些困惑:P
HyperNeutrino

运行它时,我得到的长度为1290(直到无操作开始),这是操作次数的五倍。258只是操作次数。
WhatToDo
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.