数据结构学习 棋盘问题
题目:
问题:在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。
input: 输入含有多组测试数据。 每组数据的第一行是两个正整数,n k,用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目。 n <= 8 , k <= n 当为-1 -1时表示输入结束。 随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。
output:对于每一组数据,给出一行输出,输出摆放的方案数目C (数据保证C<2^31)。
这里得用dfs 回溯 剪枝
错误点:
这道题是我在学习dfs里碰到的一道题。这道题我在做的时候,一开始没有关注棋盘的重复问题。导致做错了。具体来讲:
我写了一个这样的for循环:
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < n; ++j)
{
if (vis_r[i] == 0 && vis_c[j] == 0)
{
vis_r[i] = 1;//标记
vis_c[j] = 1;
dfs(step + 1);
vis_r[i] = 0;//回溯
vis_c[j] = 0;
}
}
}
我试图遍历棋盘上每个点,并且在确定第一个点之后,在棋盘任意位置继续找下一个摆放的点。在任意位置摆放下一个点的行为将会导致摆放方案的重复。
具体来说,比如有一个3×3的棋盘,想要放3个棋子(n=3,k=3):
1、在i=0,j=0时,机器会找到这样一种情况:【标红的是(0,0)】
1 | ||
1 | ||
1 |
?2、但是在i=1,j=1时,机器又会找到这样相同的情况,并计数:【标红的是(1,1)】
1 | ||
1 | ||
1 |
?3、紧接着,在i=2,j=2时,机器又会找到这样相同的情况,并计数:【标红的是(2,2)】
1 | ||
1 | ||
1 |
?也就是说,因为我没有做合适的剪枝,导致重复计算了,相同的摆放方法被我计数了3次。
改正的方案:
for (int i = r; i < n; ++i)
{
for (int j = 0; j < n; ++j)
{
if (vis_r[i] == 0 && vis_c[j] == 0)
{
vis_r[i] = 1;//标记
vis_c[j] = 1;
dfs(step + 1,r+1);
vis_r[i] = 0;//回溯
vis_c[j] = 0;
}
}
}
多加了一个变量r来记录可以被用来遍历的行的范围。限制下一步棋子找的范围。即:
每次都从放过棋子下一行开始搜索,保证不重复。
第一行已经被找过了i=0 | |||
循环回到step=1,从第二行(r=1)找 | 1 | ||
此时想要确定i,i只能从第三行(r+1)开始找 | 1 |
总之找到一个合适的剪枝,别让它重复了。
复杂度:
时间复杂度O(n!)
空间复杂度O(n) 两个vis
代码:
太懒了,直接写在main这了。这样写是不合适的。
#include <iostream>
#include <vector>
//棋盘问题
int n = 3;
int k = 2;
int count = 0;
int step = 1;
std::vector<int> vis_r(n);
std::vector<int> vis_c(n);
void dfs(int step, int r);
int main()
{
dfs(step, 0);
std::cout << count;
}
void dfs(int step,int r)
{
if (step == n + 1)
{
++count;
return;
}
for (int i = r; i < n; ++i)
{
for (int j = 0; j < n; ++j)
{
if (vis_r[i] == 0 && vis_c[j] == 0)
{
vis_r[i] = 1;//标记
vis_c[j] = 1;
dfs(step + 1,r+1);
vis_r[i] = 0;//回溯
vis_c[j] = 0;
}
}
}
return;
}
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!