2754 - 八皇后
问题描述:http://poj.grids.cn/practice/2754/
2754 - 八皇后
Time Limit:
1000ms
Memory limit:
65536kB
题目描述
会下国际象棋的人都很清楚:皇后可以在横、竖、斜线上不限步数地吃掉其他棋子。
如何将8个皇后放在棋盘上(有8 * 8个方格),使它们谁也不能被吃掉!这就是著名的八皇后问题。
对于某个满足要求的8皇后的摆放方法,定义一个皇后串a与之对应,即a=b1b2...b8,其中bi为相应摆法中第i行皇后所处的列数。
已经知道8皇后问题一共有92组解(即92个不同的皇后串)。给出一个数b,要求输出第b个串。
串的比较是这样的:皇后串x置于皇后串y之前,当且仅当将x视为整数时比y小。
输入
第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数b(1 <= b <= 92)
输出
输出有n行,每行输出对应一个输入。输出应是一个正整数,是对应于b的皇后串。
样例输入
2
1
92
样例输出
15863724
84136275
首先是用了迭代法,把八层循环都列了出来。。 循环太多了,各种复制粘贴和查找替换。。最后内存252kb,时间0ms
/********************************************************************************************************
解题思路:
经典方法用的是递归和回溯,穷举所有情况。我用了八层的迭代来试试,主要要找一个高效的,
判定当前棋子是否能够安全放置的方法,并且拿掉此棋子还能够还原它对其他棋子的影响。
按照每行来迭代,自然不考虑行的限制。
用一个标识数组column[8]来判断当前列是否能够放棋子,并且保存受到了几个棋子的影响,
如果受到多个棋子影响了,回溯时拿掉一个棋子,不会影响其他棋子的影响。
用另2个标识数组diagonal_1[15]和diagonal_2[15]来判断当前右斜线和左斜线分别能否放棋子。
对于棋子board[i][j],所在右斜线用diagonal_1[j-i+7]表示,左斜线用diagonal_2[i+j]表示。
内存:252kB
时间:0ms
********************************************************************************************************/
#include <iostream>
#include <cmath>
#include <cctype>
#include <string>
#include <map>
#include <set>
#include <vector>
#include <algorithm>
#include <list>
#include <stack>
#include <cstring>
//#include <stdlib.h>
//#include <iomanip>
using namespace std;
inline void add_chess(unsigned column[], unsigned diagonal_1[], unsigned diagonal_2[], int i, int j)
{
column[j]++;
diagonal_1[j-i+7]++;
diagonal_2[i+j]++;
}
inline void cancel_chess(unsigned column[], unsigned diagonal_1[], unsigned diagonal_2[], int i, int j)
{
column[j]--;
diagonal_1[j-i+7]--;
diagonal_2[i+j]--;
}
int main()
{
//存放所有结果
unsigned answers[92][8];
//存放一次结果
unsigned count = 0, hang[8] = {0};
//用来记录当前的列是否可以放棋子
unsigned column[8] = {0};
unsigned diagonal_1[15] = {0}, diagonal_2[15] = {0};
//////////////////////////////////////////////////////////////////////////
//枚举所有情况,并且剪枝+回溯
//第0层循环
for (hang[0] = 0; hang[0] < 8; hang[0]++)
{
add_chess(column, diagonal_1, diagonal_2, 0, hang[0]);
//第1层循环
for (hang[1] = 0; hang[1] < 8; hang[1]++)
{
if (column[hang[1]] == 0 && diagonal_1[hang[1]+6] == 0 && diagonal_2[hang[1]+1] == 0)
{
add_chess(column, diagonal_1, diagonal_2, 1, hang[1]);
//第2层循环
for (hang[2] = 0; hang[2] < 8; hang[2]++)
{
if (column[hang[2]] == 0 && diagonal_1[hang[2]+5] == 0 && diagonal_2[hang[2]+2] == 0)
{
add_chess(column, diagonal_1, diagonal_2, 2, hang[2]);
//第3层循环
for (hang[3] = 0; hang[3] < 8; hang[3]++)
{
if (column[hang[3]] == 0 && diagonal_1[hang[3]+4] == 0 && diagonal_2[hang[3]+3] == 0)
{
add_chess(column, diagonal_1, diagonal_2, 3, hang[3]);
//第4层循环
for (hang[4] = 0; hang[4] < 8; hang[4]++)
{
if (column[hang[4]] == 0 && diagonal_1[hang[4]+3] == 0 && diagonal_2[hang[4]+4] == 0)
{
add_chess(column, diagonal_1, diagonal_2, 4, hang[4]);
//第5层循环
for (hang[5] = 0; hang[5] < 8; hang[5]++)
{
if (column[hang[5]] == 0 && diagonal_1[hang[5]+2] == 0 && diagonal_2[hang[5]+5] == 0)
{
add_chess(column, diagonal_1, diagonal_2, 5, hang[5]);
//第6层循环
for (hang[6] = 0; hang[6] < 8; hang[6]++)
{
if (column[hang[6]] == 0 && diagonal_1[hang[6]+1] == 0 && diagonal_2[hang[6]+6] == 0)
{
add_chess(column, diagonal_1, diagonal_2, 6, hang[6]);
//第7层循环
for (hang[7] = 0; hang[7] < 8; hang[7]++)
{
if (column[hang[7]] == 0 && diagonal_1[hang[7]] == 0 && diagonal_2[hang[7]+7] == 0)
{
//添加一组解
memcpy(answers[count], hang, sizeof(unsigned)*8);
count++;
}
}
cancel_chess(column, diagonal_1, diagonal_2, 6, hang[6]);
}
}
cancel_chess(column, diagonal_1, diagonal_2, 5, hang[5]);
}
}
cancel_chess(column, diagonal_1, diagonal_2, 4, hang[4]);
}
}
cancel_chess(column, diagonal_1, diagonal_2, 3, hang[3]);
}
}
cancel_chess(column, diagonal_1, diagonal_2, 2, hang[2]);
}
}
cancel_chess(column, diagonal_1, diagonal_2, 1, hang[1]);
}
}
cancel_chess(column, diagonal_1, diagonal_2, 0, hang[0]);
}
//////////////////////////////////////////////////////////////////////////
//输入结果
int n, tmp;
cin >> n;
while (n--)
{
cin >> tmp;
for (int i = 0; i < 8; i++)
cout << answers[tmp-1][i]+1;
cout << "\n";
}
return 0;
}
解题思路:
经典方法用的是递归和回溯,穷举所有情况。我用了八层的迭代来试试,主要要找一个高效的,
判定当前棋子是否能够安全放置的方法,并且拿掉此棋子还能够还原它对其他棋子的影响。
按照每行来迭代,自然不考虑行的限制。
用一个标识数组column[8]来判断当前列是否能够放棋子,并且保存受到了几个棋子的影响,
如果受到多个棋子影响了,回溯时拿掉一个棋子,不会影响其他棋子的影响。
用另2个标识数组diagonal_1[15]和diagonal_2[15]来判断当前右斜线和左斜线分别能否放棋子。
对于棋子board[i][j],所在右斜线用diagonal_1[j-i+7]表示,左斜线用diagonal_2[i+j]表示。
内存:252kB
时间:0ms
********************************************************************************************************/
#include <iostream>
#include <cmath>
#include <cctype>
#include <string>
#include <map>
#include <set>
#include <vector>
#include <algorithm>
#include <list>
#include <stack>
#include <cstring>
//#include <stdlib.h>
//#include <iomanip>
using namespace std;
inline void add_chess(unsigned column[], unsigned diagonal_1[], unsigned diagonal_2[], int i, int j)
{
column[j]++;
diagonal_1[j-i+7]++;
diagonal_2[i+j]++;
}
inline void cancel_chess(unsigned column[], unsigned diagonal_1[], unsigned diagonal_2[], int i, int j)
{
column[j]--;
diagonal_1[j-i+7]--;
diagonal_2[i+j]--;
}
int main()
{
//存放所有结果
unsigned answers[92][8];
//存放一次结果
unsigned count = 0, hang[8] = {0};
//用来记录当前的列是否可以放棋子
unsigned column[8] = {0};
unsigned diagonal_1[15] = {0}, diagonal_2[15] = {0};
//////////////////////////////////////////////////////////////////////////
//枚举所有情况,并且剪枝+回溯
//第0层循环
for (hang[0] = 0; hang[0] < 8; hang[0]++)
{
add_chess(column, diagonal_1, diagonal_2, 0, hang[0]);
//第1层循环
for (hang[1] = 0; hang[1] < 8; hang[1]++)
{
if (column[hang[1]] == 0 && diagonal_1[hang[1]+6] == 0 && diagonal_2[hang[1]+1] == 0)
{
add_chess(column, diagonal_1, diagonal_2, 1, hang[1]);
//第2层循环
for (hang[2] = 0; hang[2] < 8; hang[2]++)
{
if (column[hang[2]] == 0 && diagonal_1[hang[2]+5] == 0 && diagonal_2[hang[2]+2] == 0)
{
add_chess(column, diagonal_1, diagonal_2, 2, hang[2]);
//第3层循环
for (hang[3] = 0; hang[3] < 8; hang[3]++)
{
if (column[hang[3]] == 0 && diagonal_1[hang[3]+4] == 0 && diagonal_2[hang[3]+3] == 0)
{
add_chess(column, diagonal_1, diagonal_2, 3, hang[3]);
//第4层循环
for (hang[4] = 0; hang[4] < 8; hang[4]++)
{
if (column[hang[4]] == 0 && diagonal_1[hang[4]+3] == 0 && diagonal_2[hang[4]+4] == 0)
{
add_chess(column, diagonal_1, diagonal_2, 4, hang[4]);
//第5层循环
for (hang[5] = 0; hang[5] < 8; hang[5]++)
{
if (column[hang[5]] == 0 && diagonal_1[hang[5]+2] == 0 && diagonal_2[hang[5]+5] == 0)
{
add_chess(column, diagonal_1, diagonal_2, 5, hang[5]);
//第6层循环
for (hang[6] = 0; hang[6] < 8; hang[6]++)
{
if (column[hang[6]] == 0 && diagonal_1[hang[6]+1] == 0 && diagonal_2[hang[6]+6] == 0)
{
add_chess(column, diagonal_1, diagonal_2, 6, hang[6]);
//第7层循环
for (hang[7] = 0; hang[7] < 8; hang[7]++)
{
if (column[hang[7]] == 0 && diagonal_1[hang[7]] == 0 && diagonal_2[hang[7]+7] == 0)
{
//添加一组解
memcpy(answers[count], hang, sizeof(unsigned)*8);
count++;
}
}
cancel_chess(column, diagonal_1, diagonal_2, 6, hang[6]);
}
}
cancel_chess(column, diagonal_1, diagonal_2, 5, hang[5]);
}
}
cancel_chess(column, diagonal_1, diagonal_2, 4, hang[4]);
}
}
cancel_chess(column, diagonal_1, diagonal_2, 3, hang[3]);
}
}
cancel_chess(column, diagonal_1, diagonal_2, 2, hang[2]);
}
}
cancel_chess(column, diagonal_1, diagonal_2, 1, hang[1]);
}
}
cancel_chess(column, diagonal_1, diagonal_2, 0, hang[0]);
}
//////////////////////////////////////////////////////////////////////////
//输入结果
int n, tmp;
cin >> n;
while (n--)
{
cin >> tmp;
for (int i = 0; i < 8; i++)
cout << answers[tmp-1][i]+1;
cout << "\n";
}
return 0;
}
然后用了经典的递归和回溯的方法,深度优先搜索一个八叉树。最后内存256kb,时间0ms
#include <iostream>
#include <cmath>
#include <cctype>
#include <string>
#include <map>
#include <set>
#include <vector>
#include <algorithm>
#include <list>
#include <stack>
#include <cstring>
//#include <stdlib.h>
//#include <iomanip>
using namespace std;
//用来记录当前的列是否可以放棋子
unsigned column[8] = {0};
unsigned diagonal_1[15] = {0}, diagonal_2[15] = {0};
//存放所有结果
unsigned answers[92][8];
//hang存放一次结果,count记录结果个数
unsigned ans_count = 0, hang[8] = {0};
inline void add_chess(int i, int j)
{
column[j]++;
diagonal_1[j-i+7]++;
diagonal_2[i+j]++;
}
//i, j分别是行和列
inline void cancel_chess(int i, int j)
{
column[j]--;
diagonal_1[j-i+7]--;
diagonal_2[i+j]--;
}
inline bool judge(int i, int j)
{
if (column[j] == 0 && diagonal_1[j-i+7] == 0 && diagonal_2[i+j] == 0)
return true;
return false;
}
void putQueen(int ith_row)
{
//则产生了一组新解,也即树的一条路径
if (ith_row == 8)
{
memcpy(answers[ans_count], hang, sizeof(unsigned)*8);
ans_count++;
}
//则查找当前行的8个棋盘格,如果可以放棋子,就放一个并且进行记录,标记对其它棋子的影响
//并且往下深度递归,递归完,回溯到这一处时,还要取消对其它棋子的影响标记
for (int j = 0; j < 8; j++)
{
if (judge(ith_row, j))
{
hang[ith_row] = j;
add_chess(ith_row, j);
putQueen(ith_row+1);
cancel_chess(ith_row, j);
}
}
}
int main()
{
//递归所有情况,并且剪枝+回溯
putQueen(0);
//输入结果
int n, tmp;
cin >> n;
while (n--)
{
cin >> tmp;
for (int i = 0; i < 8; i++)
cout << answers[tmp-1][i]+1;
cout << "\n";
}
return 0;
}
#include <cmath>
#include <cctype>
#include <string>
#include <map>
#include <set>
#include <vector>
#include <algorithm>
#include <list>
#include <stack>
#include <cstring>
//#include <stdlib.h>
//#include <iomanip>
using namespace std;
//用来记录当前的列是否可以放棋子
unsigned column[8] = {0};
unsigned diagonal_1[15] = {0}, diagonal_2[15] = {0};
//存放所有结果
unsigned answers[92][8];
//hang存放一次结果,count记录结果个数
unsigned ans_count = 0, hang[8] = {0};
inline void add_chess(int i, int j)
{
column[j]++;
diagonal_1[j-i+7]++;
diagonal_2[i+j]++;
}
//i, j分别是行和列
inline void cancel_chess(int i, int j)
{
column[j]--;
diagonal_1[j-i+7]--;
diagonal_2[i+j]--;
}
inline bool judge(int i, int j)
{
if (column[j] == 0 && diagonal_1[j-i+7] == 0 && diagonal_2[i+j] == 0)
return true;
return false;
}
void putQueen(int ith_row)
{
//则产生了一组新解,也即树的一条路径
if (ith_row == 8)
{
memcpy(answers[ans_count], hang, sizeof(unsigned)*8);
ans_count++;
}
//则查找当前行的8个棋盘格,如果可以放棋子,就放一个并且进行记录,标记对其它棋子的影响
//并且往下深度递归,递归完,回溯到这一处时,还要取消对其它棋子的影响标记
for (int j = 0; j < 8; j++)
{
if (judge(ith_row, j))
{
hang[ith_row] = j;
add_chess(ith_row, j);
putQueen(ith_row+1);
cancel_chess(ith_row, j);
}
}
}
int main()
{
//递归所有情况,并且剪枝+回溯
putQueen(0);
//输入结果
int n, tmp;
cin >> n;
while (n--)
{
cin >> tmp;
for (int i = 0; i < 8; i++)
cout << answers[tmp-1][i]+1;
cout << "\n";
}
return 0;
}