POJ1321 棋盘问题 —— DFS回溯

棋盘摆放问题解析
本文探讨了一个关于在不规则形状的棋盘上摆放棋子的问题,要求任意两个棋子不能位于同一行或同一列。通过类比八皇后问题,文章详细介绍了如何使用递归回溯算法来解决该问题,并给出了完整的C++实现代码。

题目链接:http://poj.org/problem?id=1321


棋盘问题
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 50263 Accepted: 24352

Description

在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。

Input

输入含有多组测试数据。 
每组数据的第一行是两个正整数,n k,用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目。 n <= 8 , k <= n 
当为-1 -1时表示输入结束。 
随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。 

Output

对于每一组数据,给出一行输出,输出摆放的方案数目C (数据保证C<2^31)。

Sample Input

2 1
#.
.#
4 4
...#
..#.
.#..
#...
-1 -1

Sample Output

2
1

Source




题解:

类似于八皇后问题。

由于题目要求放k个,不像八皇后那样固定了8个,所以在一开始时不知道怎样处理“k个”。即如果实际能放的位置比k要大时该怎么处理

一开始是想:在能够放的地方,执行两种操作,放和不放。但是却会出现重复,因为对于一行,不放的位置可以是多个,这就有了多种情况,但实际上这多种情况都是一样的,因为对于接下来的行,所呈现的状态是一样的。

正确的操作是:把操作对象从“一个”改为“一行”。对于每一行,有放和不放两种操作,如果不放,则直接跳过;如果放,则再决定放在哪个位置。




代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <string>
#include <set>
#define ms(a,b) memset((a),(b),sizeof((a)))
using namespace std;
typedef long long LL;
const int INF = 2e9;
const LL LNF = 9e18;
const int MOD = 1e9+7;
const int MAXN = 8+10;

int n, k, ans;
char m[MAXN][MAXN];
int col[MAXN];

void dfs(int dep, int t)
{
    if(dep==n+1)    //到达末时,如果刚好放了k个,则计数器+1
    {
        if(t==k)  ans++;
        return;
    }

    dfs(dep+1, t);  //这一行不放
    for(int i = 1; i<=n; i++)   //这一行放
    {
        if(m[dep][i]=='#' && col[i]==0 && t<k )  //t<k为剪枝
        {
            col[i] = 1;
            dfs(dep+1, t+1);
            col[i] = 0;
        }
    }

//以下为错误的思想
//    for(int i = 1; i<=n; i++)
//    {
//        if(m[dep][i]=='#' && col[i]==0 )
//        {
//            dfs(dep+1, t);    //会出现重复
//            col[i] = 1;
//            dfs(dep+1, t+1);
//            col[i] = 0;
//        }
//    }

}

int main()
{
    while(scanf("%d%d",&n,&k))
    {
        if(n==-1 && k==-1) break;

        ms(col,0);
        ans = 0;
        for(int i = 1; i<=n; i++)
            scanf("%s", m[i]+1);

        dfs(1, 0);
        cout<< ans <<endl;
    }
}






评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值