我正在尝试遵循关于算法的USACO培训课程(http://ace.delos.com/usacogate) - 我目前正处于描述DFS,BFS等的页面。我确实理解这些概念,但他们的样本问题是给予BFS - 骑士封面 - 让我感到困惑。这是问题陈述:
在一个n x n棋盘上放置尽可能少的骑士,以便每个方格都受到攻击。骑士不被认为会攻击它所在的广场。
这是BFS,页面说,因为它试图在尝试n
骑士之前看看是否有n+1
骑士的解决方案 - 这很清楚。
但是,我不明白如何单独制定解决方案。有人可以用这个伪代码帮助我吗?
提前多多谢谢!
答案 0 :(得分:7)
是BFS,但你不搜索棋盘;搜索展示位置空间:
初始状态:没有骑士
有效移动:将骑士放在任何未占用的牌上
目标状态:所有图块都被占用或受到攻击
基本算法(状态空间的BFS):
请注意,我假设状态的所有路径长度相同。在以这种方式查找一组展示位置时,情况确实如此,但一般情况下并非如此。如果不是这样,您应该存储所有访问过的节点的集合,以避免重新访问已经探索过的状态。
你可能需要从左到右,从上到下添加骑士。然后,您不需要检查队列中的重复项。此外,如果您知道在不违反广告订单的情况下无法攻击未遭受攻击的磁贴,您可以提前弃掉状态。
如果你不这样做并且也留下重复检查,算法仍然会产生正确的结果,但它会慢得多。大约慢40 000倍(8!= 40 320是8骑士状态的副本数量)。
如果您想要更快的算法,请查看A *。在这里,一种可能的启发式方法是:
更好的启发式方法会注意到一个骑士只能攻击相同颜色的瓷砖,并占据相反颜色的瓷砖。这可能会稍微改善先前的启发式(但仍有可能帮助很多)。
更好的启发式应该能够利用骑士可以覆盖不超过5x5平方的自由点的事实。启发式算法应该计算速度很快,但这可能有助于覆盖很少的点。
技术细节:
您可以将每个状态表示为64位位掩码。虽然这需要一些按位操作,但它确实有助于内存,64位数字的等式检查 fast 。如果您不能使用64位数字,请使用两个32位数字 - 这些数字应该可用。
循环阵列队列是高效的,并且 难以扩展其容量。如果您必须实现自己的队列,请选择此队列。
答案 1 :(得分:1)
这是C ++中的一个实现。
它只使用基本蛮力,所以只有n = 5
才有效。
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
bool isFinal(vector<vector<bool> >& board, int n)
{
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < n; ++j)
{
if(!board[i][j])
return false;
}
}
return true;
}
void printBoard(vector<pair<int,int> > vec, int n)
{
vector<string> printIt(n);
for(int i = 0; i < n; ++i)
{
string s = "";
for(int j = 0; j < n; ++j)
{
s += ".";
}
printIt[i] = s;
}
int m = vec.size();
for(int i = 0; i < m; ++i)
{
printIt[vec[i].first][vec[i].second] = 'x';
}
for(int i = 0; i < n; ++i)
{
cout << printIt[i] << endl;
}
cout << endl;
}
void updateBoard(vector<vector<bool> >& board, int i, int j, int n)
{
board[i][j] = true;
if(i-2 >= 0 && j+1 < n)
board[i-2][j+1] = true;
if(i-1 >= 0 && j+2 < n)
board[i-1][j+2] = true;
if(i+1 < n && j+2 < n)
board[i+1][j+2] = true;
if(i+2 < n && j+1 < n)
board[i+2][j+1] = true;
if(i-2 >= 0 && j-1 >= 0)
board[i-2][j-1] = true;
if(i-1 >= 0 && j-2 >= 0)
board[i-1][j-2] = true;
if(i+1 < n && j-2 >= 0)
board[i+1][j-2] = true;
if(i+2 < n && j-1 >= 0)
board[i+2][j-1] = true;
}
bool isThere(vector<pair<int,int> >& vec, vector<vector<pair<int,int> > >& setOfBoards, int len)
{
for(int i = 0; i < len; ++i)
{
if(setOfBoards[i] == vec)
return true;
}
return false;
}
int main()
{
int n;
cin >> n;
vector<vector<pair<int,int> > > setOfBoards;
int len = 0;
vector<vector<bool> > startingBoard(n);
for(int i = 0; i < n; ++i)
{
vector<bool> vec(n,0);
startingBoard[i] = vec;
}
vector<pair<int,int> > startingVec;
vector<vector<vector<vector<bool> > > > q1;
vector<vector<vector<pair<int,int> > > > q2;
vector<vector<vector<bool> > > sLayer1;
vector<vector<pair<int,int> > > sLayer2;
sLayer1.push_back(startingBoard);
sLayer2.push_back(startingVec);
q1.push_back(sLayer1);
q2.push_back(sLayer2);
int k = 0;
bool flag = false;
int count = 0;
while(!flag && !q1[k].empty())
{
int m = q1[k].size();
vector<vector<vector<bool> > > layer1;
vector<vector<pair<int,int> > > layer2;
q1.push_back(layer1);
q2.push_back(layer2);
for(int l = 0; l < m; ++l)
{
vector<vector<bool> > board = q1[k][l];
vector<pair<int,int> > vec = q2[k][l];
if(isFinal(board, n))
{
while(l < m)
{
board = q1[k][l];
vec = q2[k][l];
if(isFinal(board, n))
{
printBoard(vec, n);
++count;
}
++l;
}
flag = true;
break;
}
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < n; ++j)
{
if(!board[i][j])
{
pair<int,int> p;
p.first = i;
p.second = j;
vector<vector<bool> > newBoard = board;
vector<pair<int,int> > newVec = vec;
newVec.push_back(p);
updateBoard(newBoard, i, j, n);
sort(newVec.begin(), newVec.end());
if(!isThere(newVec, setOfBoards, len))
{
q1[k+1].push_back(newBoard);
q2[k+1].push_back(newVec);
setOfBoards.push_back(newVec);
++len;
}
}
}
}
}
++k;
}
cout << count << endl;
}