2n皇后问题
问题描述:
在n*n的矩阵中放入n个白皇后和n个黑皇后,该矩阵由元素0和1组成,0代表该位置不能放皇后,1代表可以放皇后,n个的皇后两两不能在同一行和同一列以及同一对 角线上,n个黑皇后两两不能在同一行和同一列以及同一对角线上。求最多有几种放法?
解法:这个问题跟8皇的问题类似,只不过多了另外一种颜色的皇后。也就是说,假设我们在矩阵上放n个皇后有m种方法,那么再次放皇后就有Cm取2种方法。由此只需在8皇后问题的代码做少些修改即可完成。以下是代码,有详细注释:
#include <iostream>
#include <vector>
#include <string.h>
using namespace std;
#define exchange(x, y) {int temp = x; x = y; y = temp;}//交换两个变量
const int N = 8;//最多输入8个值
int index[N] = {0, 1, 2, 3, 4, 5, 6, 7};//下标数组,是用下标进行排列
vector<int*> method;//存放可行的排列
void sort(int *p, int beg, int end)
{//冒泡排序
int i = 0;
for(; end > beg; end --)
for(i = beg; i < end; i++)
if(*(p + i) > *(p + i + 1))
exchange(*(p + i), *(p + i + 1));
}
void print()
{//打印结果
int i = 0;
for(i = 0; i < N; i++)
cout << index[i] << " ";
cout << endl;
}
void next_permutation(int* cur, int n)
{//得到下一次全排列
int i = 0, decIndex = n- 1, excIndex = n - 1, temp = 0;
if(cur[n - 1] > cur[n - 2])
{
exchange(cur[n - 1], cur[n - 2]);
//print();
return;
}
for(i = n - 1; i > -1; i --)
{
if(*(cur + i) > *(cur + i - 1))
{
decIndex = i - 1;//得到第一个降序数
break;
}
}
if(decIndex == -1)//假设排列已到最后
return;
temp = cur[decIndex];
for(i = decIndex + 1; i < n; i++)
{//得到要交换的数,该交换数比降序数稍大
if(cur[i] > temp)//得到比降序数大的数,比下一个数小的数
temp = cur[i];
if(cur[i] > cur[decIndex] && cur[i] <= temp)
excIndex = i;//假设该数比除序数大且比temp小或等于
}
exchange(cur[decIndex], cur[excIndex]);
sort(cur, decIndex + 1, n - 1);
//print();
}
bool judge(int index[], int n)
{//判为对角线上是否有两个相同颜色
int i = 0, j = 0, m = 1;
for(i = 0; i < n - 1; i++, m = 1)
{
for(j = i + 1; j < n; j++)
{
if((index[i] - m == index[j]) || (index[i] + m == index[j]))
return false;
m++;
}
}
return true;
}
bool fill_queen(int *p, int n)
{//填充皇后,若该位置放0则直接返回
int i = 0, j = 0;
for(i = 0; i < n; i++)
{
j = i * n + index[i];
if(!*(p + j))
return false;
if(!(judge(index, n)))
return false;
}
return true;
}
int fatorial(int n)
{//阶乘
if(n == 1)
return 1;
return n*fatorial(n - 1);
}
int solve(int *p, int n)
{
int i = 0, j = 0, count = 0, m = 0;
int *temp1 = new int[n], *temp2 = new int[n];
if(n < 4)
return 0;
for(i = 0; i < fatorial(n); i++)
{//在n的阶乘次内完成下一次全排列
next_permutation(index, n);
if(!fill_queen(p, n))
continue;//若函数返回值为假
int* temp = new int[n];
memcpy(temp, index, sizeof(int) * n);//将可行结果复制给temp并加入容器内
method.push_back(temp);
}
vector<int*>::iterator it;
vector<int*>::iterator it2;
for(it = method.begin(); it != method.end() - 1;)
{//从第一个开始与其他项作比较,若位置没有冲突则计数加1
temp1 = *it;
for(it2 = ++it; it2 != method.end(); it2++)
{
temp2 = *it2;
j = 0;
for(; j < n; j++)
{
if(*(temp1 + j) == *(temp2 + j))
break;
}
if(j == n)
count++;
}
}
return count;
}
int main()
{
//freopen("input.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
int n = 0, i = 0, resu = 0;
cin >> n;//矩阵为N阶矩阵
int* arr = new int[n * n];
for(i = 0; i < n * n; i++)
cin >> arr[i];//输入矩阵的0和1值
resu = solve(arr, n);
cout << resu * 2;//结果乘以2就是所得Cm取2的值
//system("pause");
return 0;
}