C++ 回溯-用硬币填充网格
我试图回答我在查找面试问题时遇到的问题。我们被问及在n*m网格上放置r硬币的方法,以便每行和每列至少包含一枚硬币 我想到了一个回溯解决方案,以行的主要顺序处理网格中的每个单元格,我用这种方式设置了递归。似乎我的方法是错误的,因为它每次输出0。有人能帮我找出我的方法中的错误吗?谢谢 限制。n,m<200,rC++ 回溯-用硬币填充网格,c++,algorithm,backtracking,C++,Algorithm,Backtracking,我试图回答我在查找面试问题时遇到的问题。我们被问及在n*m网格上放置r硬币的方法,以便每行和每列至少包含一枚硬币 我想到了一个回溯解决方案,以行的主要顺序处理网格中的每个单元格,我用这种方式设置了递归。似乎我的方法是错误的,因为它每次输出0。有人能帮我找出我的方法中的错误吗?谢谢 限制。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;
}
#包括
#定义N 201
int n,m,r;
int使用[N][N];
整数网格[N][N];//1是硬币被放置。否则为0-1未决定。
bool isOk()
{
int行[N];
int cols[N];
对于(inti=0;i=n)返回0;
如果(col>=m)返回0;
如果(硬币>r)返回0;
如果(硬币==r)
{
bool res=isOk();
如果(res)返回1;
其余0;
}
如果(行==n-1,列==m-1)
{
bool res=isOk();
如果(res)返回1;
否则返回0;
}
int nrow,ncol;
如果(列+1>=m)
{
nrow=行+1;
ncol=0;
}
其他的
{
nrow=行;
ncol=col+1;
}
如果(使用[行][列])返回计算方式(nrow、ncol、硬币);
int ans=0;
使用的[行][列]=1;
网格[行][列]=0;
ans+=计算方式(nrow、ncol、硬币);
网格[行][列]=1;
ans+=计算方式(nrow、ncol、硬币+1);
返回ans;
}
int main()
{
int t;
scanf(“%d”、&t);
而(t--)
{
scanf(“%d%d%d”,&n,&m,&r);
对于(int i=0;i问题1
代码将首先在每个方块上放置一枚硬币,并将每个方块标记为已使用
然后,它将测试最终位置,并确定最终位置不符合r硬币的目标
下一步,它将开始回溯,但实际上永远不会尝试其他选择,因为used[row][col]设置为1,这会缩短放置硬币的代码
换句话说,一个问题是设置了“used”中的条目,但在递归过程中从未清除
问题2
代码的另一个问题是,如果n,m的大小为200,那么它将永远不会完成
问题是这种回溯代码的复杂性为O(2^(n*m)),因为它将尝试所有可能的硬币放置组合(n=m=200的许多宇宙寿命…)
我建议你看一个不同的方法。例如,你可能想考虑动态编程来计算有多少种方法将“K”硬币放置在板的“A”栏上,这样我们就可以把硬币放在“B”上。目前没有硬币的一排棋盘。
你几乎不需要一个程序来解决这个问题
在不丧失一般性的情况下,设m它可以被视为d网格b填充r硬币的总方式-(保留一行的总方式nd填充d rest-保留一列的总方式nd填充d rest-保留一行和一列的总方式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)) )
我只是这么想,但不确定!我会看看如何解决八皇后问题,然后将其修改为这个更通用的版本。可能是一个小错误,但在(int j=0;j中
不应该是m
而不是n
吗?@jeffamaphone:你能详细解释一下这个评论吗?我真的不知道这会有什么用。@DesmondHume谢谢你。这不是我0输出的原因:)那么你是否建议当我检查r币的目标时,我应该使用[row][col col]失败?我根本不明白所用数组的用途。也许你可以删除所有对它的引用?我在做dp时遇到了麻烦。你能不能再详细说明一些提示???非常感谢。首先计算一下在一列中放置“x”硬币的方式。(有一个简单的表达式涉及阶乘。)经过思考,包含排除可能会更快,甚至可能有一个封闭式公式。如果您需要更多信息,我建议发布一个新问题,以便更多的人可以提供帮助(我想我在错误的时区有效地帮助您…)
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)) )