C# 编程竞赛题:计算普立米诺斯
请看我自己的答案,我想我做到了强>C# 编程竞赛题:计算普立米诺斯,c#,java,c++,algorithm,C#,Java,C++,Algorithm,请看我自己的答案,我想我做到了 嗨 编程竞赛的一个示例问题是编写一个程序,找出给定数量的石头可以产生多少Polyminos 因此,对于两块石头(n=2),只有一块Polyminos: XX 您可能会认为这是第二种解决方案: X X 但事实并非如此。如果可以旋转Polyminos,则它们不是唯一的 因此,对于4块石头(n=4),有7种解决方案: X X XX X X X X X X XX X XX XX XX X X X
嗨 编程竞赛的一个示例问题是编写一个程序,找出给定数量的石头可以产生多少Polyminos 因此,对于两块石头(
n=2
),只有一块Polyminos:
XX
您可能会认为这是第二种解决方案:
X
X
但事实并非如此。如果可以旋转Polyminos,则它们不是唯一的
因此,对于4块石头(n=4
),有7种解决方案:
X
X XX X X X X
X X XX X XX XX XX
X X X XX X X XX
应用程序必须能够找到1=0的解决方案--十)
{
对于(int y=0;y<矩阵[x]。长度;++y)
{
if(矩阵[x][y])
{
右转;
}
}
r++;
}
//顶
top:for(int y=0;y<矩阵[0]。长度;++y)
{
对于(int x=0;x=0;--y)
{
对于(int x=0;x hash+=矩阵[x][y]?((x+7)最简单的解决方案是从一个x
开始,对于每个迭代,构建唯一可能的下一个状态的列表。从该列表中,通过添加另一个x
来构建唯一状态的列表。继续此操作直到您想要的迭代
但是,我不确定这是否在N=10的合理时间内运行。这可能取决于您的要求。只有4461个大小为10的多项式,因此我们可以将它们全部列举出来
从一块石头开始。若要将其扩展一块石头,请尝试将新石头添加到与现有石头相邻的所有空单元格中。重复此操作,直到达到所需大小
为了避免重复,请保留一个哈希表,其中包含我们已经列举过的每种大小的所有多项式。当我们组合一个新的多项式时,我们检查它是否不在哈希表中。我们还需要检查它的3次旋转(可能还有它的镜像).虽然最终大小的重复检查是唯一严格必要的检查,但每一步的检查都会修剪递归分支,从而生成新的多项式
下面是一些伪代码:
polynomino = array of n hashtables
function find_polynominoes(n, base):
if base.size == n:
return
for stone in base:
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
new_stone.x = stone.x + dx
new_stone.y = stone.y + dy
if new_stone not in base:
new_polynomino = base + new_stone
is_new = true
for rotation in [0, 90, 180, 270]:
if new_polynomino.rotate(rotation) in polynomino[new_polynomino.size]:
is_new = false
break
if is_new:
polynomino[new_polynomino.size].add(new_polynomino)
我想我做到了
编辑:我正在使用SHA-256
算法对它们进行散列,现在它工作正常了
结果如下:
numberOfStones -> numberOfPolyominos
1 -> 1
2 -> 1
3 -> 2
4 -> 7
5 -> 18
6 -> 60
7 -> 196
8 -> 704
9 -> 2500
10 -> terminated
下面是代码(Java):
导入java.io.BufferedReader;
导入java.io.IOException;
导入java.io.InputStreamReader;
导入java.util.ArrayList;
导入java.util.List;
/*VPW模板*/
公共班机
{
/**
*@param args
*/
公共静态void main(字符串[]args)引发IOException
{
新建Main().start();
}
public void start()引发IOException
{
/*阅读材料*/
BufferedReader br=新的BufferedReader(新的InputStreamReader(System.in));
字符串[]输入=新字符串[Integer.parseInt(br.readLine())];
对于(int i=0;i0&!矩阵[x-1][y])
{
布尔[][]克隆=复制(矩阵);
克隆[x-1][y]=true;
CreatePolyMinos(克隆,n-1);
}
if(x0&!矩阵[x][y-1])
{
布尔[][]克隆=复制(矩阵);
克隆[x][y-1]=真;
CreatePolyMinos(克隆,n-1);
}
if(y<矩阵[x].长度-1&!矩阵[x][y+1])
{
布尔[][]克隆=复制(矩阵);
克隆[x][y+1]=真;
CreatePolyMinos(克隆,n-1)
numberOfStones -> numberOfPolyominos
1 -> 1
2 -> 1
3 -> 2
4 -> 7
5 -> 18
6 -> 60
7 -> 196
8 -> 704
9 -> 2500
10 -> terminated
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
/* VPW Template */
public class Main
{
/**
* @param args
*/
public static void main(String[] args) throws IOException
{
new Main().start();
}
public void start() throws IOException
{
/* Read the stuff */
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] input = new String[Integer.parseInt(br.readLine())];
for (int i = 0; i < input.length; ++i)
{
input[i] = br.readLine();
}
/* Process each line */
for (int i = 0; i < input.length; ++i)
{
processLine(input[i]);
}
}
public void processLine(String line)
{
int n = Integer.parseInt(line);
System.out.println(countPolyminos(n));
}
private int countPolyminos(int n)
{
hashes.clear();
count = 0;
boolean[][] matrix = new boolean[n][n];
matrix[n / 2][n / 2] = true;
createPolyominos(matrix, n - 1);
return count;
}
private List<BigInteger> hashes = new ArrayList<BigInteger>();
private int count;
private void createPolyominos(boolean[][] matrix, int n)
{
if (n == 0)
{
boolean[][] cropped = cropMatrix(matrix);
BigInteger hash = hashMatrixOrientationIndependent(cropped);
if (!hashes.contains(hash))
{
// System.out.println(count + " Found!");
// printMatrix(cropped);
// System.out.println();
count++;
hashes.add(hash);
}
return;
}
for (int x = 0; x < matrix.length; ++x)
{
for (int y = 0; y < matrix[x].length; ++y)
{
if (matrix[x][y])
{
if (x > 0 && !matrix[x - 1][y])
{
boolean[][] clone = copy(matrix);
clone[x - 1][y] = true;
createPolyominos(clone, n - 1);
}
if (x < matrix.length - 1 && !matrix[x + 1][y])
{
boolean[][] clone = copy(matrix);
clone[x + 1][y] = true;
createPolyominos(clone, n - 1);
}
if (y > 0 && !matrix[x][y - 1])
{
boolean[][] clone = copy(matrix);
clone[x][y - 1] = true;
createPolyominos(clone, n - 1);
}
if (y < matrix[x].length - 1 && !matrix[x][y + 1])
{
boolean[][] clone = copy(matrix);
clone[x][y + 1] = true;
createPolyominos(clone, n - 1);
}
}
}
}
}
public boolean[][] copy(boolean[][] matrix)
{
boolean[][] b = new boolean[matrix.length][matrix[0].length];
for (int i = 0; i < matrix.length; ++i)
{
System.arraycopy(matrix[i], 0, b[i], 0, matrix[i].length);
}
return b;
}
public void printMatrix(boolean[][] matrix)
{
for (int y = 0; y < matrix.length; ++y)
{
for (int x = 0; x < matrix[y].length; ++x)
{
System.out.print((matrix[y][x] ? 'X' : ' '));
}
System.out.println();
}
}
public boolean[][] cropMatrix(boolean[][] matrix)
{
int l = 0, t = 0, r = 0, b = 0;
// Left
left: for (int x = 0; x < matrix.length; ++x)
{
for (int y = 0; y < matrix[x].length; ++y)
{
if (matrix[x][y])
{
break left;
}
}
l++;
}
// Right
right: for (int x = matrix.length - 1; x >= 0; --x)
{
for (int y = 0; y < matrix[x].length; ++y)
{
if (matrix[x][y])
{
break right;
}
}
r++;
}
// Top
top: for (int y = 0; y < matrix[0].length; ++y)
{
for (int x = 0; x < matrix.length; ++x)
{
if (matrix[x][y])
{
break top;
}
}
t++;
}
// Bottom
bottom: for (int y = matrix[0].length - 1; y >= 0; --y)
{
for (int x = 0; x < matrix.length; ++x)
{
if (matrix[x][y])
{
break bottom;
}
}
b++;
}
// Perform the real crop
boolean[][] cropped = new boolean[matrix.length - l - r][matrix[0].length - t - b];
for (int x = l; x < matrix.length - r; ++x)
{
System.arraycopy(matrix[x], t, cropped[x - l], 0, matrix[x].length - t - b);
}
return cropped;
}
public BigInteger hashMatrix(boolean[][] matrix)
{
try
{
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update((byte) matrix.length);
md.update((byte) matrix[0].length);
for (int x = 0; x < matrix.length; ++x)
{
for (int y = 0; y < matrix[x].length; ++y)
{
if (matrix[x][y])
{
md.update((byte) x);
} else
{
md.update((byte) y);
}
}
}
return new BigInteger(1, md.digest());
} catch (NoSuchAlgorithmException e)
{
System.exit(1);
return null;
}
}
public BigInteger hashMatrixOrientationIndependent(boolean[][] matrix)
{
BigInteger hash = hashMatrix(matrix);
for (int i = 0; i < 3; ++i)
{
matrix = rotateMatrixLeft(matrix);
hash = hash.add(hashMatrix(matrix));
}
return hash;
}
public boolean[][] rotateMatrixRight(boolean[][] matrix)
{
/* W and H are already swapped */
int w = matrix.length;
int h = matrix[0].length;
boolean[][] ret = new boolean[h][w];
for (int i = 0; i < h; ++i)
{
for (int j = 0; j < w; ++j)
{
ret[i][j] = matrix[w - j - 1][i];
}
}
return ret;
}
public boolean[][] rotateMatrixLeft(boolean[][] matrix)
{
/* W and H are already swapped */
int w = matrix.length;
int h = matrix[0].length;
boolean[][] ret = new boolean[h][w];
for (int i = 0; i < h; ++i)
{
for (int j = 0; j < w; ++j)
{
ret[i][j] = matrix[j][h - i - 1];
}
}
return ret;
}
#!/usr/bin/python
# compute the canonical representation of polyomino p.
# (minimum x and y coordinate is zero, sorted)
def canonical(p):
mx = min(map(lambda v: v[0], p))
my = min(map(lambda v: v[1], p))
return sorted(map(lambda v: (v[0]-mx, v[1]-my), p))
# rotate p 90 degrees
def rotate(p):
return canonical(map(lambda v: (v[1], -v[0]), p))
# add one tile to p
def expand(p):
result = []
for (x,y) in p:
for (dx,dy) in ((-1,0),(1,0),(0,-1),(0,1)):
if p.count((x+dx,y+dy)) == 0:
result.append(canonical(p + [(x+dx,y+dy)]))
return result
polyominos = [[(0,0)]]
for i in xrange(1,10):
new_polyominos = []
for p in polyominos:
for q in expand(p):
dup = 0
for r in xrange(4):
if new_polyominos.count(q) != 0:
dup = 1
break
q = rotate(q)
if not dup: new_polyominos.append(q)
polyominos = new_polyominos
print i+1, len(polyominos)
numberOfStones -> numberOfPolyominos
1 -> 1
2 -> 1
3 -> 2
4 -> 7
5 -> 18
6 -> 60
7 -> 196
8 -> 704 (3 seconds)
9 -> 2500 (46 seconds)
10 -> 9189 (~14 minutes)
/*
* This class is a solution to the Tetris unique shapes problem.
* That is, the game of Tetris has 7 unique shapes. These 7 shapes
* are all the possible unique combinations of any 4 adjoining blocks
* (i.e. ignoring rotations).
*
* How many unique shapes are possible with, say, 7 or n blocks?
*
* The solution uses recursive back-tracking to construct all the possible
* shapes. It uses a HashMap to store unique shapes and to ignore rotations.
* It also uses a temporary HashMap so that the program does not needlessly
* waste time checking the same path multiple times.
*
* Even so, this is an exponential run-time solution, with n=10 taking a few
* minutes to complete.
*/
package com.glugabytes.gbjutils;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class TetrisBlocks {
private HashMap uShapes;
private HashMap tempShapes;
/* Get a map of unique shapes for n squares. The keys are string-representations
* of each shape, and values are corresponding boolean[][] arrays.
* @param squares - number of blocks to use for shapes, e.g. n=4 has 7 unique shapes
*/
public Map getUniqueShapes(int squares) {
uShapes = new HashMap();
tempShapes = new HashMap();
boolean[][] data = new boolean[squares*2+1][squares*2+1];
data[squares][squares] = true;
make(squares, data, 1); //start the process with a single square in the center of a boolean[][] matrix
return uShapes;
}
/* Recursivelly keep adding blocks to the data array until number of blocks(squares) = required size (e.g. n=4)
* Make sure to eliminate rotations. Also make sure not to enter infinite backtracking loops, and also not
* needlessly recompute the same path multiple times.
*/
private void make(int squares, boolean[][] data, int size) {
if(size == squares) { //used the required number of squares
//get a trimmed version of the array
boolean[][] trimmed = trimArray(data);
if(!isRotation(trimmed)) { //if a unique piece, add it to unique map
uShapes.put(arrayToString(trimmed), trimmed);
}
} else {
//go through the grid 1 element at a time and add a block next to an existing block
//do this for all possible combinations
for(int iX = 0; iX < data.length; iX++) {
for(int iY = 0; iY < data.length; iY++) {
if(data[iX][iY] == true) { //only add a block next to an existing block
if(data[iX+1][iY] != true) { //if no existing block to the right, add one and recuse
data[iX+1][iY] = true;
if(!isTempRotation(data)) { //only recurse if we haven't already been on this path before
make(squares, data, size+1);
tempShapes.put(arrayToString(data), data); //store this path so we don't repeat it later
}
data[iX+1][iY] = false;
}
if(data[iX-1][iY] != true) { //repeat by adding a block on the left
data[iX-1][iY] = true;
if(!isTempRotation(data)) {
make(squares, data, size+1);
tempShapes.put(arrayToString(data), data);
}
data[iX-1][iY] = false;
}
if(data[iX][iY+1] != true) { //repeat by adding a block down
data[iX][iY+1] = true;
if(!isTempRotation(data)) {
make(squares, data, size+1);
tempShapes.put(arrayToString(data), data);
}
data[iX][iY+1] = false;
}
if(data[iX][iY-1] != true) { //repeat by adding a block up
data[iX][iY-1] = true;
if(!isTempRotation(data)) {
make(squares, data, size+1);
tempShapes.put(arrayToString(data), data);
}
data[iX][iY-1] = false;
}
}
}
}
}
}
/**
* This function basically removes all rows and columns that have no 'true' flags,
* leaving only the portion of the array that contains useful data.
*
* @param data
* @return
*/
private boolean[][] trimArray(boolean[][] data) {
int maxX = 0;
int maxY = 0;
int firstX = data.length;
int firstY = data.length;
for(int iX = 0; iX < data.length; iX++) {
for (int iY = 0; iY < data.length; iY++) {
if(data[iX][iY]) {
if(iY < firstY) firstY = iY;
if(iY > maxY) maxY = iY;
}
}
}
for(int iY = 0; iY < data.length; iY++) {
for (int iX = 0; iX < data.length; iX++) {
if(data[iX][iY]) {
if(iX < firstX) firstX = iX;
if(iX > maxX) maxX = iX;
}
}
}
boolean[][] trimmed = new boolean[maxX-firstX+1][maxY-firstY+1];
for(int iX = firstX; iX <= maxX; iX++) {
for(int iY = firstY; iY <= maxY; iY++) {
trimmed[iX-firstX][iY-firstY] = data[iX][iY];
}
}
return trimmed;
}
/**
* Return a string representation of the 2D array.
*
* @param data
* @return
*/
private String arrayToString(boolean[][] data) {
StringBuilder sb = new StringBuilder();
for(int iX = 0; iX < data.length; iX++) {
for(int iY = 0; iY < data[0].length; iY++) {
sb.append(data[iX][iY] ? '#' : ' ');
}
sb.append('\n');
}
return sb.toString();
}
/**
* Rotate an array clockwise by 90 degrees.
* @param data
* @return
*/
public boolean[][] rotate90(boolean[][] data) {
boolean[][] rotated = new boolean[data[0].length][data.length];
for(int iX = 0; iX < data.length; iX++) {
for(int iY = 0; iY < data[0].length; iY++) {
rotated[iY][iX] = data[data.length - iX - 1][iY];
}
}
return rotated;
}
/**
* Checks to see if two 2d boolean arrays are the same
* @param a
* @param b
* @return
*/
public boolean equal(boolean[][] a, boolean[][] b) {
if(a.length != b.length || a[0].length != b[0].length) {
return false;
} else {
for(int iX = 0; iX < a.length; iX++) {
for(int iY = 0; iY < a[0].length; iY++) {
if(a[iX][iY] != b[iX][iY]) {
return false;
}
}
}
}
return true;
}
public boolean isRotation(boolean[][] data) {
//check to see if it's a rotation of a shape that we already have
data = rotate90(data); //+90*
String str = arrayToString(data);
if(!uShapes.containsKey(str)) {
data = rotate90(data); //180*
str = arrayToString(data);
if(!uShapes.containsKey(str)) {
data = rotate90(data); //270*
str = arrayToString(data);
if(!uShapes.containsKey(str)) {
return false;
}
}
}
return true;
}
public boolean isTempRotation(boolean[][] data) {
//check to see if it's a rotation of a shape that we already have
data = rotate90(data); //+90*
String str = arrayToString(data);
if(!tempShapes.containsKey(str)) {
data = rotate90(data); //180*
str = arrayToString(data);
if(!tempShapes.containsKey(str)) {
data = rotate90(data); //270*
str = arrayToString(data);
if(!tempShapes.containsKey(str)) {
return false;
}
}
}
return true;
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
TetrisBlocks tetris = new TetrisBlocks();
long start = System.currentTimeMillis();
Map shapes = tetris.getUniqueShapes(8);
long end = System.currentTimeMillis();
Iterator it = shapes.keySet().iterator();
while(it.hasNext()) {
String shape = (String)it.next();
System.out.println(shape);
}
System.out.println("Unique Shapes: " + shapes.size());
System.out.println("Time: " + (end-start));
}
}
import itertools
from collections import defaultdict
n = 10
print("Number of Tetris pieces up to size", n)
# Times:
# n is number of blocks
# - Python O(exp(n)^2): 10 blocks 2.5m
# - Python O(exp(n)): 10 blocks 2.5s, 11 blocks 10.9s, 12 block 33s, 13 blocks 141s (800MB memory)
smallest_piece = [(0, 0)] # We represent a piece as a list of block positions
pieces_of_size = {
1: [smallest_piece],
}
# Returns a list of all possible pieces made by adding one block to given piece
def possible_expansions(piece):
# No flatMap in Python 2/3:
# https://stackoverflow.com/questions/21418764/flatmap-or-bind-in-python-3
positions = set(itertools.chain.from_iterable(
[(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)] for (x, y) in piece
))
# Time complexity O(n^2) can be improved
# For each valid position, append to piece
expansions = []
for p in positions:
if not p in piece:
expansions.append(piece + [p])
return expansions
def rotate_90_cw(piece):
return [(y, -x) for (x, y) in piece]
def canonical(piece):
min_x = min(x for (x, y) in piece)
min_y = min(y for (x, y) in piece)
res = sorted((x - min_x, y - min_y) for (x, y) in piece)
return res
def hash_piece(piece):
return hash(tuple(piece))
def expand_pieces(pieces):
expanded = []
#[
# 332322396: [[(1,0), (0,-1)], [...]],
# 323200700000: [[(1,0), (0,-2)]]
#]
# Multimap because two different pieces can happen to have the same hash
expanded_hashes = defaultdict(list)
for piece in pieces:
for e in possible_expansions(piece):
exp = canonical(e)
is_new = True
if exp in expanded_hashes[hash_piece(exp)]:
is_new = False
for rotation in range(3):
exp = canonical(rotate_90_cw(exp))
if exp in expanded_hashes[hash_piece(exp)]:
is_new = False
if is_new:
expanded.append(exp)
expanded_hashes[hash_piece(exp)].append(exp)
return expanded
for i in range(2, n + 1):
pieces_of_size[i] = expand_pieces(pieces_of_size[i - 1])
print("Pieces with {} blocks: {}".format(i, len(pieces_of_size[i])))