一、八皇后问题
八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。
//朴素检查
/*好奇怪的是
我用朴素检查的时候 n<10时的运行时间
比绝对值算法效率低
而当n>12开始绝对值算法的效率开始比朴素算法的效率低
错觉?
使用逐层遍历
1.不用对该点下方进行检查,所以可以省去对下方的查找
*/
#include<algorithm>
#include<iostream>
#include<string>
using namespace std;
#define N 10000
int map[N][N];
int ans[N];
int m, n;
int check(int x,int y)
{
for (int i = 1; i <= x; i++)
if (map[i][y])
return 0;
for (int i = x - 1, j = y - 1; i >= 1 && j >= 1; j--,i--)
if (map[i][j])
return 0;
for (int i = x - 1, j = y + 1; i >= 1 && j <= n; j++, i--)
if (map[i][j])
return 0;
return 1;
}
void dfs(int x)
{
if (x == n+1)
{
for (int i = 1; i <= n; i++)
printf("%d", ans[i]);
printf("\n");
m++;
return;
}
for (int i = 1; i <= n; i++)
{
if (check(x, i))
{
ans[x] = i;
map[x][i] = 1;
dfs(x + 1);
map[x][i] = 0;
}
}
}
int main()
{
while (cin >> n)
{
memset(map, 0, sizeof(map));
m = 0;
dfs(1);
printf("%d\n", m);
}
return 0;
}
//行列规律检查
//这个算法的效率高很多
#include<iostream>
#include<ctime>
using namespace std;
int n,m;
bool y[41], x1[41], x2[41];
int ans[21];
void dfs(int x)
{
if (x == n + 1)
{
m++;
return;
}
for (int i = 1; i <= n; i++)
{
if (y[i] == 0 && x1[x + i] == 0 && x2[x - i + n]==0)
{
ans[x] = i;
y[i] = 1;
x1[x + i] = 1;
x2[x - i + n] = 1;
dfs(x + 1);
x1[x + i] = 0;
y[i] = 0;
x2[x - i + n] = 0;
}
}
}
int main()
{
while (cin >> n)
{
m = 0;
memset(ans, 0, sizeof(ans));
memset(y, 0, sizeof(y));
memset(x1, 0, sizeof(x1));
memset(x2, 0, sizeof(x2));
dfs(1);
printf("%d\n", m);
}
return 0;
}
//算法检查
#include<algorithm>
#include<iostream>
#include<string>
#include<ctime>
using namespace std;
#define N 21
int ans[N];
int m, n;
int check(int x,int y)
{
int j = 1;
while (j < x)
{
if (ans[j] == y || abs(x - j) == abs(ans[j] - y))
return 0;
j++;
}
return 1;
}
void dfs(int x)
{
if (x == n+1)
{
/*for (int i = 1; i <= n; i++)
printf("%d", ans[i]);
printf("\n");*/
m++;
return;
}
for (int i = 1; i <= n; i++)
{
if (check(x, i))
{
ans[x] = i;
dfs(x + 1);
}
}
}
int main()
{
while (cin >> n)
{
clock_t start, finish;
start = clock();
memset(ans, 0, sizeof(ans));
m = 0;
dfs(1);
printf("%d\n", m);
finish = clock();
cout << "\n" << finish - start << "/" << CLOCKS_PER_SEC << " (s) " << endl;
}
return 0;
}
//对称算法
/*
改进1
*/
#include <iostream>
#include <math.h>
using namespace std;
int ans[20 + 1];
char x1[40 + 1];//斜线数为2*N+1用来判断正斜线"/"的
char x2[40 + 1];//反斜线"\"上有棋,则作标记
char lie[20 + 1];//解决列冲突,如果第i列有棋,则lie[i]=1,放下一棋时直接查看lie[i]即可
long m, n;
void dfs(int x, int k)
{
int i;
if (x == n + 1)
m++;
else
{
for (i = 1; i <= k; i++)
{
if (!lie[i] && !x1[x + i] && !x2[x - i + n])
{
ans[x] = i;
lie[i] = 1;
x1[i + x] = 1;
x2[x - i + n] = 1;
if (n % 2 == 1 && i == (n + 1) / 2 && x == 1)
dfs(2, (n + 1) / 2 - 1);
else
dfs(x + 1, n);
lie[i] = 0;
x1[i + x] = 0;
x2[x - i + n] = 0;
}
}
}
}
int main()
{
while (cin >> n)
{
m = 0;
memset(ans, 0, sizeof(ans));
memset(lie, 0, sizeof(lie));
memset(x1, 0, sizeof(x1));
memset(x2, 0, sizeof(x2));
dfs(1,(n+1)/2);
printf("%d\n",2*m);
}
return 0;
}
//~取反运算符
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
int sum;
int lim;
void queen(int row,int left,int right)
{
if(row==lim)
{
sum++;
return ;
}
int pos,p;
pos=lim&~(row | left | right);
while(pos)
{
p=pos&(-pos);
pos-=p;
queen(row+p,(left+p)<<1,(right+p)>>1);
}
}
int main()
{
int n;
while(cin>>n)
{
sum=0;
lim=(1<<n)-1;
queen(0,0,0);
printf("%d\n",sum);
}
return 0;
}