アドベントチャレンジ1:サンタが現在の金庫をアンロックできるように助けてください!


18

次へ>>

記述キーワード(検索用):2つの行列を同等にする、重複、配列、検索

チャレンジ

サンタは過去にエルフがボールトからプレゼントを盗んだという歴史があったため、今年は非常に割れにくいロックを設計し、今年はエルフを締め出したようです。残念ながら、彼は組み合わせを失い、それを開く方法もわかりません!幸いなことに、彼はあなたにその組み合わせを見つけるプログラムを書くように依頼しました。それは最短のものである必要はありませんが、できるだけ早く見つける必要があります!

彼には非常に厳しいスケジュールがあり、非常に長い間待つ余裕はありません。スコアは、プログラムの合計ランタイムに、スコアリング入力に対してプログラムが出力するステップ数を掛けたものになります。最低スコアが勝ちます。

仕様書

ロックは1と0の正方行列です。1と0のランダムな配置に設定され、指定されたコードに設定する必要があります。幸いなことに、サンタは必要なコードを覚えています。

彼が実行できるいくつかのステップがあります。各ステップは任意の連続したサブマトリックスで実行できます(つまり、左上隅と右下隅で完全に囲まれているサブマトリックスを選択する必要があります)(非正方形のサブマトリックスにすることができます):

  1. 右に90度回転*
  2. 左に90度回転*
  3. 180度回転
  4. 各行n要素を右または左に循環(ラップ)
  5. 各列m要素を上下に循環(ラップ)
  6. 水平に反転
  7. 垂直に反転
  8. 主対角線で反転*
  9. メインの対角線を反転*

*サブマトリックスが正方形の場合のみ

もちろん、彼はマトリックス全体でこれらのステップを実行することもできます。1と0はマトリックス上でのみ交換できますが、正方形の値は直接変更できないため、1と0の数は開始構成と終了構成で同じです。

フォーマット仕様とルール

入力は、必要な妥当な形式の2つの正方行列(開始位置と終了位置)として与えられます。出力は、読み取り可能な形式のこれらの手順のシーケンスである必要があります。これはコードゴルフではないので、簡単に検証可能な形式にしてください。しかし、それは厳密な要件ではありません。必要に応じて、入力の行列の辺の長さを選択できます。

あなたのプログラムは私のコンピューターで実行され(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列をサブマトリックスとして取ります。各列を下に循環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⁶¹到達可能な合計マトリックスがあります。(ただしはるかに少ない教授のキューブよりメガミンクスと同等であり、ルービックリベンジよりも大きい)
user202729

回答:


1

Java

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);
	}
}

わずかに高速なハードコードバージョン:オンラインで試してみてください!

入力は、コマンドラインを介したスペースで区切られた整数です。最初の整数は、2つの行列の幅です。残りの整数は、行ごとの要素です。

マトリックスのすべての順列は、水平フリップ演算子と垂直フリップ演算子だけで取得できるため、同じ領域の連続するvFlipとhFlipを180度の回転に置き換えることを除いて、残りを無視しました。

プログラムは各要素をスキャンします。間違ったビットを持つ要素に遭遇するたびに、正しいビットを持つスポットを見つけるために、配列を先読みします。検索領域を2つに分割しました。列座標が等しいか大きいものと、列座標が小さいものです。後者は、配列を走査する方法に基づいて、より大きな行座標を持つ必要があることに注意してください。最初の検索領域で正しいビットが見つかった場合、合計1回の操作で2つの要素にまたがるサブマトリックスを180度回転するだけで済みます。2番目の領域にある場合は、水平反転を使用して正しいビットを間違ったビットと同じ列に移動し、2つにまたがるサブマトリックスを垂直に反転して合計2つの操作を実行できます。

プログラムの出力は、5つのグループに精神的に分割される配列です。各グループは(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(no-opsが開始するまで)になります。これは、操作の数の5倍です。258は操作の数です。
WhatToDo
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.