📅  最后修改于: 2023-12-03 15:41:09.930000             🧑  作者: Mango
问题12是须藤放置问题系列中的一个问题。该问题是要求在一个N x N的方格中放置M个黑色方块和W个白色方块,使得每个L x L的正方形中恰好包含一个黑色格子和一个白色格子。该问题涉及到组合数学中的排列组合问题,需要用到高精度计算。
该问题中每个L x L的正方形中必须恰好包含一个黑色格子和一个白色格子,即每个正方形的黑白格子比例为1:1。因此,M和W必须满足以下条件:
M + W = N x N
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]);
}
}
}