这个问题要求我们找出在一个N*M的网格上放置R个硬币的方式数量,使得每一行和每一列都至少有一个硬币。给定的限制条件是N,M < 200,R < N*M。我最初考虑使用回溯算法,但我意识到它永远不会在时间内完成。有人能指导我另外一种解决方案吗?(DP,封闭式公式)。任何提示都将不胜感激。谢谢。
这个问题要求我们找出在一个N*M的网格上放置R个硬币的方式数量,使得每一行和每一列都至少有一个硬币。给定的限制条件是N,M < 200,R < N*M。我最初考虑使用回溯算法,但我意识到它永远不会在时间内完成。有人能指导我另外一种解决方案吗?(DP,封闭式公式)。任何提示都将不胜感激。谢谢。
根据OEIS序列A055602,其中一种可能的解决方案是:
Let a(m, n, r) = Sum_{i=0..m} (-1)^i*binomial(m, i)*binomial((m-i)*n, r)
Answer = Sum_{i=0..N} (-1)^i*binomial(N, i)*a(M, N-i, R)
您需要评估N+1个不同的a值。
假设您已经预先计算了二项式系数,每次对a的评估都是O(M),因此总复杂度为O(NM)。
这个公式可以使用包含排除原理两次推导出来。
a(m,n,r)是将r个硬币放在大小为m*n的网格上的方法数,使得每个m列都被占用,但并非所有行都必须被占用。
包含-排除将其转化为正确的答案。(思路是我们从a(M,N,R)中获得第一个估计值。这会高估正确答案,因为并非所有行都被占用,所以我们减去只占用N-1行的情况a(M,N-1,R)。然后这会低估,所以我们需要再次进行修正...)
同样地,我们可以通过考虑 b(m,n,r) 来计算 a(m,n,r),其中 b(m,n,r) 是将 r 枚硬币放置在网格上的方式数,而我们不关心行或列是否被占用。这可以简单地从在 m*n 的网格大小中选择 r 个位置的方式数(即二项式(m*n,r))中推导出来。我们使用 IE 将其转换为函数 a(m,n,r),其中我们知道所有列都被占用。这很困难,但如果你先计算出每行和每列至少有一枚硬币的方式数量(称为保留硬币),答案将是 #1 (n! / r! (n - r)!) *
的乘积,其中 #2 n = N*M - NUMBER_OF_RESERVE_COINS
,#3 r = (R - NUMBER_OF_RESERVE_COINS)
,对于 #4 每个保留一枚硬币的排列。
#4 是更棘手的部分。对于 N*M 其中 N!=M,abs(N-M)
告诉你单行/列上会有多少个保留硬币。我在确定下一步正确的处理方式方面遇到了麻烦,主要是由于时间不足(尽管我可以在周末回来),但我希望我提供的信息对你有用,并且如果我所说的是正确的,你将能够完成这个过程。