我正在尝试在查找面试问题时遇到这个问题。我们被问到将r币放在n * m网格上的方式的数量,使得每行和每列包含至少一个硬币。
我想到了一个回溯解决方案,按行主要顺序处理网格中的每个单元格,我已经以这种方式设置了我的递归。似乎我的方法有问题,因为它每次都输出0。有人可以帮我找到我的方法中的错误。 ?感谢。
约束。 n,m< 200和r< n * m个;
这是我想出的代码。
#include<cstdio>
#define N 201
int n, m , r;
int used[N][N];
int grid[N][N] ; // 1 is coin is placed . 0 otherwise. // -1 undecided.
bool isOk()
{
int rows[N];
int cols[N];
for(int i = 0 ; i < n ; i++) rows[i] = 0;
for(int i = 0 ; i < m ; i++) cols[i] = 0;
int sum = 0;
for(int i = 0 ; i < n ; i++)for(int j = 0; j < m ; j++)
{
if(grid[i][j]==1)
{
rows[i]++;
cols[j]++;
sum++;
}
}
for(int i = 0 ; i < n ; i++)
{
if(rows[i]==0) return false;
}
for(int j = 0 ; j < n ; j++)
{
if(cols[j]==0) return false;
}
if(sum==r) return true;
else return false;
}
int calc_ways(int row , int col, int coins)
{
if(row >= n) return 0;
if(col >= m) return 0;
if(coins > r) return 0;
if(coins == r)
{
bool res = isOk();
if(res) return 1;
else 0;
}
if(row == n - 1 and col== m- 1)
{
bool res = isOk();
if(res) return 1;
else return 0;
}
int nrow, ncol;
if(col + 1 >= m)
{
nrow = row + 1;
ncol = 0;
}
else
{
nrow = row;
ncol = col + 1;
}
if(used[row][col]) return calc_ways(nrow, ncol, coins);
int ans = 0;
used[row][col] = 1;
grid[row][col] = 0;
ans += calc_ways(nrow , ncol , coins);
grid[row][col] = 1;
ans += calc_ways(nrow , ncol , coins + 1);
return ans;
}
int main()
{
int t;
scanf("%d" , &t);
while(t--)
{
scanf("%d %d %d" , &n , &m , &r);
for(int i = 0 ; i <= n ; i++)
{
for(int j = 0; j <= m ; j++)
{
used[i][j] = 0;
grid[i][j] = -1;
}
}
printf("%d\n" , calc_ways(0 , 0 , 0 ));
}
return 0;
}
答案 0 :(得分:3)
你几乎不需要一个程序来解决这个问题。
不失一般性,让m&lt; = n。
首先,我们必须有n&lt; = r,否则无法解决问题。
然后,我们将问题细分为一个大小为mxm的正方形,我们将沿主要对角线放置m个硬币,剩下的就是我们将放置n-m个硬币的剩余部分,以便满足剩余条件
有一种方法可以将硬币放在广场的主要对角线上。
余数有m ^(n - m)种可能性。 到目前为止,我们可以在n中排列总数!方式,虽然其中一些将是重复的(剩下多少作为学生的练习)。
此外,还有剩下的r - n个硬币和剩下的(m - 1)n个地方。
将这些全部放在一起我们有
的上限1 x m^(n - m) x n! x C((m - 1)n, r - n)
问题的解决方案。将此数字除以重复排列的数量,您就完成了。
答案 1 :(得分:0)
代码将首先在每个正方形上放置一枚硬币并标记每个正方形。
然后测试最终位置并确定最终位置不符合r币的目标。
接下来它将开始回溯,但实际上永远不会尝试其他选择,因为使用[row] [col]设置为1并且这会使代码短路以放置硬币。
换句话说,一个问题是“used”中的条目已设置,但在递归期间从未清除。
代码的另一个问题是,如果n,m的大小为200,那么它将永远不会完成。
问题是这个回溯代码具有复杂度O(2 ^(n * m)),因为它将尝试放置硬币的所有可能组合(n = m = 200的许多宇宙生命周期......)。
我建议你看一下不同的方法。例如,您可能需要考虑动态编程来计算在板的剩余“a”列上放置“k”硬币的方式,以便我们确保将硬币放在“b”行的“b”行上。目前没有硬币的董事会。
答案 2 :(得分:0)
它可以被视为d网格可以用r硬币填充的总方式 - (总的方式留下单行和填充d休息 - 总体方式留下单个列并填充d休息 - 总方式离开行nd列和nd填充d rest)暗示
p(n*m ,r) -( (p((n-1)*m , r) * c(n,1)) +(p((m-1)*n , r) * c(m,1))+(p((n-1)*(m-1) , r) * c(n,1)*c(m,1)) )
我只是这么认为,但不确定!