Advent Challenge 1: ช่วยซานต้าปลดล็อกห้องนิรภัยของเขาในปัจจุบัน!


18

ถัดไป >>

คำหลักที่สื่อความหมาย (สำหรับการค้นหา): สร้างสอง Matrices Equivalent, Overlap, Array, Find

ท้าทาย

ซานต้ามีประวัติของเอลฟ์ที่ขโมยของขวัญจากห้องนิรภัยของเขาในอดีตดังนั้นในปีนี้เขาได้ออกแบบกุญแจที่ยากต่อการถอดรหัส น่าเสียดายที่เขาสูญเสียชุดค่าผสมและเขาไม่สามารถหาวิธีเปิดได้! โชคดีที่เขาจ้างคุณให้เขียนโปรแกรมเพื่อค้นหาชุดค่าผสม ไม่จำเป็นต้องสั้นที่สุด แต่เขาต้องการค้นหาให้เร็วที่สุด!

เขามีตารางงานที่เข้มงวดมากและเขาไม่สามารถรอได้นานมาก คะแนนของคุณจะเป็นจำนวนรันไทม์ทั้งหมดของโปรแกรมของคุณคูณด้วยจำนวนขั้นตอนที่โปรแกรมของคุณแสดงผลสำหรับอินพุตการให้คะแนน คะแนนต่ำสุดชนะ

ข้อมูลจำเพาะ

ล็อคเป็นเมทริกซ์จตุรัส 1s และ 0s มันถูกตั้งค่าเป็นการจัดเรียงแบบสุ่มของ 1s และ 0s และต้องถูกตั้งค่าเป็นรหัสที่ระบุ โชคดีที่ซานต้าจดจำรหัสที่ต้องการได้

มีไม่กี่ขั้นตอนที่เขาสามารถทำได้ แต่ละขั้นตอนสามารถดำเนินการกับเมทริกซ์ย่อยที่ต่อเนื่องกัน (นั่นคือคุณต้องเลือกเมทริกซ์ย่อยที่ล้อมรอบทั้งหมดด้วยมุมบนซ้ายและขวาล่าง) (อาจเป็นเมทริกซ์ย่อยที่ไม่ใช่สแควร์):

  1. หมุนไปทางขวา 90 องศา *
  2. หมุนซ้าย 90 องศา *
  3. หมุน 180 องศา
  4. วนรอบnองค์ประกอบแต่ละแถวทางซ้ายหรือขวา (แรป)
  5. วนmองค์ประกอบแต่ละคอลัมน์ขึ้นหรือลง (wraps)
  6. พลิกแนวนอน
  7. พลิกในแนวตั้ง
  8. พลิกบนเส้นทแยงมุมหลัก *
  9. พลิกบนหลักต่อต้านเส้นทแยงมุม *

* เฉพาะในกรณีที่เมทริกซ์ย่อยเป็นจตุรัส

แน่นอนว่าเขาสามารถทำตามขั้นตอนเหล่านี้กับเมทริกซ์ทั้งหมดได้ เนื่องจาก 1s และ 0s สามารถสลับบนเมทริกซ์ได้เท่านั้น แต่ค่าของจตุรัสไม่สามารถเปลี่ยนแปลงได้โดยตรงจำนวน 1s และ 0s จึงเหมือนกันสำหรับการกำหนดค่าเริ่มต้นและสิ้นสุด

ข้อกำหนดการจัดรูปแบบ + กฎ

คุณจะได้รับอินพุตเป็นเมทริกซ์สี่เหลี่ยมสองตัว (ตำแหน่งเริ่มต้นและตำแหน่งสิ้นสุด) ในรูปแบบที่เหมาะสมที่คุณต้องการ เอาต์พุตควรเป็นลำดับของขั้นตอนเหล่านี้ในรูปแบบที่อ่านได้ เนื่องจากนี่ไม่ใช่โค้ดกอล์ฟโปรดทำให้เป็นรูปแบบที่ตรวจสอบได้ง่าย แต่นั่นไม่ใช่ข้อกำหนดที่เข้มงวด คุณสามารถเลือกใช้ความยาวด้านข้างของเมทริกซ์ในอินพุตถ้าคุณต้องการ

โปรแกรมของคุณจะถูกเรียกใช้บนคอมพิวเตอร์ของฉัน (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)

ช่องโหว่มาตรฐานใช้ จะไม่ยอมรับคำตอบ การเข้ารหัสที่มีความสุข!

หมายเหตุ: ผมดึงแรงบันดาลใจสำหรับชุดความท้าทายนี้จากการจุติของรหัส ฉันไม่มีส่วนเกี่ยวข้องกับเว็บไซต์นี้

คุณสามารถดูรายการของความท้าทายทั้งหมดในซีรีส์โดยดูที่ส่วน 'เชื่อมโยง' ของความท้าทายแรกที่นี่


ข้อมูล: กรณีทดสอบมี 192 0และ 64 1และมี256 choose 64 ≈ 1.9 × 10⁶¹เมทริกซ์ที่เข้าถึงได้ทั้งหมด (ซึ่งเปรียบได้กับ Megaminx และมีขนาดใหญ่กว่า Rubik's Revenge แม้ว่าจะน้อยกว่าลูกบาศก์ของศาสตราจารย์)
user202729

คำตอบ:


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

รุ่นฮาร์ดโค้ดที่เร็วกว่าเล็กน้อย: ลองออนไลน์!

อินพุตคือจำนวนเต็มคั่นด้วยช่องว่างผ่านบรรทัดคำสั่ง จำนวนเต็มแรกคือความกว้างของเมทริกซ์สองตัว จำนวนเต็มที่เหลืออยู่คือองค์ประกอบของแต่ละแถว

ทุกการเปลี่ยนแปลงของเมทริกซ์สามารถทำได้ด้วยตัวดำเนินการพลิกแนวนอนและแนวตั้งดังนั้นฉันจึงไม่สนใจส่วนที่เหลือยกเว้นการเปลี่ยน vFlip และ hFlip ติดต่อกันในภูมิภาคเดียวกันด้วยการหมุน 180 องศา

โปรแกรมสแกนแต่ละองค์ประกอบ เมื่อใดก็ตามที่เราพบองค์ประกอบที่มีบิตผิดมันจะมองไปข้างหน้าผ่านอาร์เรย์เพื่อค้นหาจุดที่มีบิตที่ถูกต้อง ฉันได้แบ่งขอบเขตการค้นหาออกเป็นสองส่วน: เขตที่มีค่าพิกัดคอลัมน์เท่ากันหรือใหญ่กว่าและพื้นที่ที่มีพิกัดคอลัมน์ขนาดเล็กกว่า โปรดทราบว่าหลังต้องมีการประสานงานแถวที่ใหญ่กว่าโดยขึ้นอยู่กับวิธีการที่เราสำรวจผ่านอาร์เรย์ หากเราพบบิตที่ถูกต้องในขอบเขตการค้นหาแรกเราสามารถหมุนเมทริกซ์ย่อยได้ 180 องศาซึ่งครอบคลุมทั้งสององค์ประกอบเพื่อการดำเนินการทั้งหมดเดียว หากอยู่ในภูมิภาคที่สองเราสามารถใช้การพลิกแนวนอนเพื่อย้ายบิตที่ถูกต้องไปยังคอลัมน์เดียวกันกับบิตที่ไม่ถูกต้องจากนั้นจึงพลิกเมทริกซ์ย่อยที่ทอดสองทั้งแนวตั้งเพื่อรวมการดำเนินการสองอย่าง

ผลลัพธ์ของโปรแกรมคืออาร์เรย์ที่ควรแบ่งจิตใจเป็นห้ากลุ่ม แต่ละกลุ่มคือ (i, row1, col1, row2, col2) โดยที่ฉันคือ 0 สำหรับ no-op, 1 สำหรับการหมุน 180 องศา, 2 สำหรับพลิกแนวนอนและ 3 สำหรับพลิกแนวตั้ง ส่วนประกอบที่เหลืออีก 4 รายการอธิบายภูมิภาคที่การดำเนินการทำงานอยู่ ฉันไม่แน่ใจว่านี่เป็นรูปแบบที่อ่านได้หรือไม่

สำหรับกรณีทดสอบที่กำหนดฉันได้รับการปฏิบัติการ 258 ครั้งและสองถึงสามมิลลิวินาทีบนคอมพิวเตอร์ของฉัน


@Erik the Outgolfer มันไม่ได้ถูกระบุและการเข้ารหัสอย่างหนักทำให้ง่ายต่อการตัดสิน
WhatToDo

ฉันได้เปลี่ยนเป็นรับข้อมูลจากบรรทัดคำสั่ง
WhatToDo

รูปแบบผลลัพธ์นี้สมเหตุสมผลเพียงพอ ฉันได้รับ 1,000 หมายเลขในอาร์เรย์ (200 การดำเนินการ?) ดังนั้น 258 มาจากไหน ฉันสับสนเล็กน้อยเกี่ยวกับวิธีอ่านผลลัพธ์จากสิ่งนี้: P
HyperNeutrino

เมื่อฉันเรียกใช้ฉันได้รับความยาว 1290 (จนกระทั่งไม่มีการเริ่มต้น) ซึ่งเป็นห้าเท่าของจำนวนการดำเนินการ 258 เป็นเพียงจำนวนของการดำเนินงาน
WhatToDo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.