如何计算一个数字的无序分区数

时间:2014-09-17 19:29:12

标签: java

我真的遇到了这个问题。

在这个问题中,您将获得一个2xN的电路板。您需要以这样的方式在此板中填写非负数:

  1. 填写的所有数字的总和= N
  2. 两行中的每一行 由不递增顺序的数字组成
  3. 每个N. 列由非递增顺序的数字组成。
  4. 考虑到数字N,可以通过多少种方式完成? 如果电路板中有一个具有不同数字的单元,则认为两种方式不同。

    输出应该是矩阵可以形成的方式的数量。 矩阵可以具有重复数字并且可以使用零。矩阵不应该有增加的数字,但是可以相互并排填充相等的数字。

    示例:

    输入 - > 5

    输出 - > 16

3 个答案:

答案 0 :(得分:3)

从你的例子(input = 5,output = 16)我想只允许整数。

一种天真(强力)解决方案是使用回溯算法:

http://en.wikipedia.org/wiki/Backtracking

在此网站上,您可以看到填充数独板的示例,直到找到解决方案。

==

例如:

您有大小为2N的整数数组。

For position 0 you take first free number. 
If solution is not broken yet you go to position 1 of array.
If solution is broken - stop as cannot back anymore

  For position 1 you take next free number.
  If solution is not broken you you go to position 2 of array.
  If solution is broken you back to previous sten and take next free number.

    For position 2...

这通常通过递归来完成。 我认为,在每个位置(递归级别),数字可以从池0..N。

中获取

尝试 - 祝你好运。

修改

这是有效的解决方案(使用回溯算法):

private final int N = 5;

// 2 rows in one array [0..N-1, N..2N-1]
private int[] board = new int[2 * N];

// found solution counter
int found = 0;

/*
 * this method set next number to current position
 * and recursively go to next position.
 */
public void check(int position) {

    // if board is complete - check if valid
    if (position == 2 * N) {
        if (isValid()) {
            System.out.println("foun : " + Arrays.toString(board));
            found++;
        }
        return;
    }


    // if board is not complete - put all numbers (0..N) into current position
    // and recursively go to next position
    for (int v = 0; v <= N; v++) {
        board[position] = v;

        // if solution is already broken - step backwards
        // see: backtracking algorithms
        if (isBroken(position)) {
            return;
        }

        check(position + 1);
    }
}

public boolean isValid() {

    // condition 1
    int sum = 0;
    for (int i = 0; i < board.length; i++) {
        sum += board[i];
    }
    if (sum != N) {
        return false;
    }

    // conditin 2
    int prev = board[0];
    for (int i = 1; i < N; i++) {
        if (board[i] > prev) {
            return false;
        }
        prev = board[i];
    }
    prev = board[N];
    for (int i = N + 1; i < 2 * N; i++) {
        if (board[i] > prev) {
            return false;
        }
        prev = board[i];
    }

    // condition 3
    for (int i = 0; i < N; i++) {
        int top = board[i];
        int bottom = board[i + N];
        if (top < bottom) {
            return false;
        }
    }

    // valid
    return true;
}

// simplified version of this method - but correct
public boolean isBroken(int current) {
    int sum = 0;
    for (int i = 0; i <= current; i++) {
        sum += board[i];
    }
    return sum > N;
}

public void start() {
    check(0);
    System.out.println("found: " + found);
}

N = 5的程序输出:

found : [1, 1, 1, 0, 0, 1, 1, 0, 0, 0]
found : [1, 1, 1, 1, 0, 1, 0, 0, 0, 0]
found : [1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
found : [2, 1, 0, 0, 0, 1, 1, 0, 0, 0]
found : [2, 1, 0, 0, 0, 2, 0, 0, 0, 0]
found : [2, 1, 1, 0, 0, 1, 0, 0, 0, 0]
found : [2, 1, 1, 1, 0, 0, 0, 0, 0, 0]
found : [2, 2, 0, 0, 0, 1, 0, 0, 0, 0]
found : [2, 2, 1, 0, 0, 0, 0, 0, 0, 0]
found : [3, 0, 0, 0, 0, 2, 0, 0, 0, 0]
found : [3, 1, 0, 0, 0, 1, 0, 0, 0, 0]
found : [3, 1, 1, 0, 0, 0, 0, 0, 0, 0]
found : [3, 2, 0, 0, 0, 0, 0, 0, 0, 0]
found : [4, 0, 0, 0, 0, 1, 0, 0, 0, 0]
found : [4, 1, 0, 0, 0, 0, 0, 0, 0, 0]
found : [5, 0, 0, 0, 0, 0, 0, 0, 0, 0]
found: 16

答案 1 :(得分:0)

这里有一种强力方式:因为这是一个2xN矩阵,并且所有数字之和必须为N,最简单的解决方案是用1&#39; s填充第一行,用0&填充第2行#39; S。现在你需要一个递归算法,它采用一个有效的板,并从任何&#34; free&#34;中删除1。职位并将其添加到任何法律职位。该板也是一个解决方案。通过&#34;免费&#34;我的意思是位置[i,j]的数字n,其中[i + 1,j]&lt; = n - 1和[i,j + 1]&lt; = n - 1.然后你递归地调用新的算法董事会,并保存一切。

剩下的就是重复数据删除解决方案。

输入5的算法示例: 初步解决方案:

11111
00000

唯一的#34;免费&#34;数字是[0,4]。删除1,唯一的合法位置是[0,0]和[0,1]。这为您提供了2个新解决方案

21110
00000

11110
10000

现在再次对这两种解决方案应用相同的算法。请注意,第二块板现在有2&#34; free&#34;数字。重复,直到你到达

50000
00000
编辑:这个例子编写了很多乐趣。没有对它进行测试,但那是我的头脑所在:

public void TwoRowBoard()
{
    var board = new int[2, N];
    //Create initial, simplest solution.
    for (int i = 0; i < N; i++)
    {
        board[0, i] = 1;
    }

    var solutions = new List<int[,]>();
    RecursiveSolve(board, solutions);
}

private void RecursiveSolve(int[,] board, List<int[,]> solutions)
{
    var freeNumbers = GetFreeNumbers(board);
    foreach (var freeNumber in freeNumbers)
    {
        board[freeNumber.i, freeNumber.j] -= 1;
        var legalPositions = GetLegalPositions(board);

        foreach (var legalPosition in legalPositions)
        {
            var newBoard = Copy(board);
            newBoard[legalPosition.i, legalPosition.j] += 1;
            solutions.Add(newBoard);
            RecursiveSolve(newBoard, solutions);
        }
    }
}

private List<Coordinates> GetLegalPositions(int[,] board)
{
    //Position 0, 0 is always legal.
    var results = new List<Coordinates> {new Coordinates {i = 0, j = 0}};

    //Row 0
    for (int j = 1; j < N; j++)
    {
        if (board[0, j - 1] > board[0, j])
        {
            results.Add(new Coordinates{i = 0, j = j});
        }
    }

    //Row 1. Board[1, higher than N/2] are never legal positions.
    for (int j = 0; j <= N /2; j++)
    {
        if (board[1, j - 1] > board[1, j]
            && board[0, j] > board[1, j])
        {
            results.Add(new Coordinates{i = 1, j = j});
        }
    }

    return results;
}


private List<Coordinates> GetFreeNumbers(int[,] board)
{
    var results = new List<Coordinates>();
    for (int i = 0; i < 2; i++)
    {
        for (int j = 0; j < N; j++)
        {
            if (i == 0 && j == 0)
            {
                continue;
            }
            if (i == 0)
            {
                if (j == N - 1 && board[0, j] > 0)
                {
                    results.Add(new Coordinates {i = 0, j = j});
                }
                else if (board[0, j] > board[1, j]
                         && board[0, j] > board[0, j + 1])
                {
                    results.Add(new Coordinates {i = 0, j = j});
                }
            }
            else
            {
                if (j > N/2 && board[1, j] > 0)
                {
                    throw new Exception("Don't see how it's possible for board[1, N/2 or higher] to not be 0");
                }
                if (board[1, j] > board[1, j + 1])
                {
                    results.Add(new Coordinates{i = 1, j = j});
                }
            }
        }
    }
    return results;
}

public class Coordinates
{
    public int i { get; set; }
    public int j { get; set; }
}

答案 2 :(得分:0)

我几乎肯定这有封闭形式的解决方案,或类似于封闭形式的解决方案的另一个问题。我会以递归方式进行编程。

所以假设你知道N的解决方案。你想要N + 1。所以你应该做的是采取N的所有解决方案,看看你可以在哪里坚持额外的1,而不会破坏任何限制。也就是说,超级将N个解决方案强加在N + 1板上然后尝试在所有2N个位置添加1而不破坏约束。然后将它们全部存储在一个集合中,这样它们就会被删除。

无论如何,它与http://en.wikipedia.org/wiki/Partition_(number_theory)

类似