📜  算法测验|须藤放置[1.5] |问题12(1)

📅  最后修改于: 2023-12-03 15:41:09.930000             🧑  作者: Mango

算法测验|须藤放置[1.5] |问题12

简介

问题12是须藤放置问题系列中的一个问题。该问题是要求在一个N x N的方格中放置M个黑色方块和W个白色方块,使得每个L x L的正方形中恰好包含一个黑色格子和一个白色格子。该问题涉及到组合数学中的排列组合问题,需要用到高精度计算。

算法分析

该问题中每个L x L的正方形中必须恰好包含一个黑色格子和一个白色格子,即每个正方形的黑白格子比例为1:1。因此,M和W必须满足以下条件:

  1. M + W = N x N

  2. M % (L x L) == W % (L x L) == 0

在满足以上条件的前提下,我们可以通过组合数学中的排列组合算法计算方案数。具体地,我们可以计算出有多少种黑格子的排列方式,然后将其乘以白格子的排列方式,即可得到总方案数。

为了处理大数,我们需要使用高精度计算。可以采用字符串表示大数并模拟手算的方式进行计算。具体地,我们可以按位进行计算,将每一位上的进位和当前位的计算结果保存在一个数组中。

代码实现

以下是该算法的代码实现。其中,BigInt表示高精度计算的大数实现,Combination表示组合数学中的排列组合实现。

import java.math.BigInteger;

public class Solution {
    public long getNumberOfWays(int N, int M, int L) {
        // calculate total number of black and white blocks
        int W = N * N - M;
        if (M % (L * L) != W % (L * L))
            return 0;

        Combination comb = new Combination(L * L);
        BigInt[][] dp = new BigInt[M / (L * L) + 1][W / (L * L) + 1];
        for (int i = 0; i <= M / (L * L); i++) {
            for (int j = 0; j <= W / (L * L); j++) {
                dp[i][j] = new BigInt(0);
            }
        }
        dp[0][0] = new BigInt(1);
        for (int i = 1; i <= M / (L * L); i++) {
            for (int j = 0; j <= W / (L * L); j++) {
                BigInt sum = new BigInt(0);
                for (int k = 0; k <= j; k++) {
                    sum = sum.add(dp[i - 1][j - k].multiply(comb.getCombination(M - i * L * L + k * L * L, k * L * L))
                            .multiply(comb.getCombination(W - j * L * L + (k - j) * L * L, (k - j) * L * L)));
                }
                dp[i][j] = sum;
            }
        }
        return dp[M / (L * L)][W / (L * L)].getValue().longValue();
    }

    static class BigInt {
        private final BigInteger value;
        private final int CAPACITY = 9;
        private final int RADIX = 1000000000;

        public BigInt(int n) {
            this.value = BigInteger.valueOf(n);
        }

        public BigInt(String s) {
            this.value = new BigInteger(s);
        }

        public BigInt add(BigInt other) {
            return new BigInt(this.value.add(other.value).toString());
        }

        public BigInt multiply(BigInt other) {
            return new BigInt(this.value.multiply(other.value).toString());
        }

        public BigInteger getValue() {
            return this.value;
        }

        @Override
        public String toString() {
            String s = this.value.toString();
            StringBuilder sb = new StringBuilder();
            for (int i = s.length() - 1; i >= 0; i -= CAPACITY) {
                int j = i - CAPACITY + 1;
                if (j < 0)
                    j = 0;
                sb.append(s.substring(j, i + 1));
                sb.append(",");
            }
            sb.deleteCharAt(sb.length() - 1);
            return sb.reverse().toString();
        }
    }

    static class Combination {
        private final int n;
        private final BigInt[] fact;
        private final BigInt[] inv;

        public Combination(int n) {
            this.n = n;
            this.fact = new BigInt[n + 1];
            this.inv = new BigInt[n + 1];
            fact[0] = new BigInt(1);
            for (int i = 1; i <= n; i++) {
                fact[i] = fact[i - 1].multiply(new BigInt(i));
            }
            inv[n] = fact[n].getValue().modInverse(BigInteger.valueOf(BigInt.RADIX));
            for (int i = n - 1; i >= 0; i--) {
                inv[i] = inv[i + 1].multiply(new BigInt(i + 1));
            }
        }

        public BigInt getCombination(int n, int m) {
            if (n < m || m < 0)
                return new BigInt(0);
            return fact[n].multiply(inv[m]).multiply(inv[n - m]);
        }
    }
}