问题描述
在一个n*n的棋盘中,每个格子中至多放置一个车,且要保证任何两个车都不能相互攻击,有多少中放法(车与车之间是没有差别的)
输入格式
包含一个正整数n
输出格式
一个整数,表示放置车的方法数
样例输入
2
样例输出
7
数据规模和约定
n<=8
【样例解释】一个车都不放为1种,放置一个车有4种,放置2个车有2种。
首先在这里需要先补充一下题目意思,题目对于不能相互攻击没有一个准确的定义,这里我们参考N皇后问题,N皇后问题要求2个皇后不能放在同一行或者同一列或者同意斜线上。根据样例解释,当棋盘大小为2时,放置两个车有两种,我们可以推测题目应该是要求两个车不能放在同一行或者同一列。
明确题意之后,我们开始解题。乍一眼看到这个问题,学过算法的小伙伴肯定很熟悉,这和我们的N皇后问题简直如出一辙(恭喜你,迈出了最关键的一步),但仔细一看,发现这题目有点别的东西哟!
N后问题使用的是回溯法,这道题其实只是在回溯法外面再套一层循环而已,为什么这么说呢?
N后问题,皇后的数量和棋盘的大小是一样的,每个皇后占一列,因此行索引是固定的,解空间(x1,x2…xn)的取值是列索引。而对该问题来说,由于车的数量小于等于棋盘大小,当车数量小于棋盘大小时,小车占的行是一个组合数。到这里,问题似乎变得明了了些。我们只需要对小车数量从0~n循环,确定小车数量后,利用回溯法,对列搜索(和N后问题一样),讲列搜索结果和组合数相乘即可。举个例子,当棋盘大小为3,小车数量为2时,可以选择的行的情况是
C
3
2
C_{3}^{2}
C32(1、2行;1、3行、2、3行)。回溯法对列搜索如图所示
代码
#include <iostream>
using namespace std;
static int* x;//解空间
static int sum=0;
static int n;
//约束条件,列的约束
bool yueshu(int t) //当前x0-xt-1都已经确定了列
{
for(int i=0; i<t; i++)
{
if(x[t]==x[i]) return false;
}
return true;
}
//回溯法得到每个xi的值
void backtrack(int t,int num)
{
// x[i]的取值是0~n-1
if(t==num) //产生一种新方案
{
sum++;
}
else
{
for(int i=0; i<n; i++)//可以选择棋盘中的任意一列,因此为n
{
x[t]=i;
if(yueshu(t)){
backtrack(t+1,num);
}
}
}
return;
}
int jiechen(int n){
int r=1;
for(int i=1;i<=n;i++){
r=r*i;
}
return r;
}
int main()
{
//n*n棋盘格,一个大的循环,确定放的小车的数量s,s<n;
//解向量元素xi(i<s)的值代表车i放在哪一列,即i确定了行数,xi确定了列数,得到的小车数量为n时的放置方案数r;
//由于小车的数量s小于等于n,因此r*C n s
cin >>n;
// n=2;
x=new int[n];//由于最多可以放n辆车,因此解向量的大小为n
int result=0;//方法总数,sum是某一次
for(int s=0; s<=n; s++)//放1辆车一直到放n辆车
{
backtrack(0,s);
result+=sum*jiechen(n)/(jiechen(s)*jiechen(n-s));
for(int i=1;i<n;i++){//需要讲解空间数值清除,sum也要重新计数
x[i]=-1;
}
sum=0;
}
cout << result;
}