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;xhash+=矩阵[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])))