A*算法:bfs + 启发式搜索(比普通搜索多一个估价函数)。
IDA* : 迭代加深 + dfs + 启发式搜索。
IDA*相较于A*:
1.不需要判重,不需要排序,利于深度剪枝。
2.空间需求减少:每个深度下实际上是一个深度优先搜索,不过深度有限制,使用 DFS 可以减小空间消耗。
3.重复搜索:即使前后两次搜索相差微小,回溯过程中每次深度变大都要再次从头搜索。
P1379 八数码难题
这题好像不怎么优化也能过,但我还是用了A*算法。
题目求给定的数组变成目标数组需要的最少操作数(操作:只能交换'0'和与它相邻的位置)。
由题意得出:估价函数=当前数组和目标数组在相同位置有多少个不同的+当前操作数。
重要的就是估价函数,其他跟bfs没差。
int dx[4] = {0, 0, -1, 1};
int dy[4] = {1, -1, 0, 0};
char df[4][4] = {"123", "804", "765"}; //目标数组
struct mair {
char st[4][4];
bool operator < (const mair& t) const { //因为该类型要作为map的key,要求有序,所以需要重载
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if(st[i][j] != t.st[i][j]) return st[i][j] < t.st[i][j];
}
}
return false;
}
}nf; //初始数组
int H(mair s) { //除'0'外与目标数组在相同位置有多少个不同的(因为我加上'0'不相等的时候WA了)
int ans = 0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if(s.st[i][j] != df[i][j] && s.st[i][j] != '0') ans++;
}
}
return ans;
}
int F(mair s, int t) { //估价函数 : f = h + g, g 是当前步数
return H(s) + t;
}
struct node {
mair s;
int t;
bool operator <(const node& temp) const {
return F(s, t) > F(temp.s, temp.t);
}
};
priority_queue<node>q; //搜索队列
map<mair, bool>vis; //标记该数组是否出现
void bfs() {
q.push({nf, 0});
vis[nf] = true;
while(!q.empty()) {
node p = q.top();
q.pop();
if(H(p.s) == 0) {
cout << p.t << endl;
return;
}
int x = -1, y = -1;
for (int i = 0; i < 3; i++) { //找到空格
for (int j = 0; j < 3; j++) {
if(p.s.st[i][j] == '0') {
x = i, y = j;
break;
}
}
if(x != -1) break;
}
p.t++;
for (int i = 0; i < 4; i++) {
int x1 = x + dx[i];
int y1 = y + dy[i];
if(x1 >= 0 && x1 < 3 && y1 >= 0 && y1 < 3) {
swap(p.s.st[x1][y1], p.s.st[x][y]); //交换
if(!vis[p.s]) {
q.push(p);
vis[p.s] = true;
}
swap(p.s.st[x][y], p.s.st[x1][y1]); //换回来
}
}
}
}
void solve() {
string s;
cin >> s;
int x = 0, y = 0;
for (int i = 0; i < s.size(); i++) {
nf.st[x][y] = s[i];
y++;
if((i + 1) % 3 == 0) {
x++;
y = 0;
}
}
bfs();
}
(这题用IDA*也可以做,这里就不写了)
P2324 [SCOI2005] 骑士精神
移动:骑士与空格交换。
跟八数码差不多,只是这题规定了在15步之内(像极了迭代加深IDA*,但是现在我用A*)。
int dx[8] = { -2,-2,-1,-1,2,2,1,1 };
int dy[8] = { 1,-1,2,-2,1,-1,2,-2 };
char df[6][6] = {"11111", "01111", "00*11", "00001", "00000"};
struct mairx {
char st[6][6];
bool operator <(const mairx& t) const {
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
if(st[i][j] != t.st[i][j]) return st[i][j] > t.st[i][j];
}
}
return false;
}
};
int h(mairx x) {
int cnt = 0;
for (int i = 0; i < 5; i++) { //跟八数码一样的h函数
for (int j = 0; j < 5; j++) {
if(x.st[i][j] != df[i][j] && x.st[i][j] != '*') cnt++;
}
}
return cnt;
}
struct node {
mairx s;
int t;
bool operator <(const node& temp) const {
return t + h(s) > temp.t + h(temp.s);
}
};
mairx nx;
void bfs() {
priority_queue<node>q;
map<mairx, bool>vis;
q.push({nx, 0});
vis[nx] = true;
while(!q.empty()) {
node p = q.top();
q.pop();
if(h(p.s) + p.t > 15) continue; //若当前与目标数组不同个数+当前步数>15就不用继续搜了
if(h(p.s) == 0) {
cout << p.t << endl;
return;
}
int x = -1, y;
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
if(p.s.st[i][j] == '*') {
x = i, y = j;
break;
}
}
if(x != -1) break;
}
p.t++;
for (int i = 0; i < 8; i++) {
int x1 = x + dx[i];
int y1 = y + dy[i];
if(x1 >= 0 && x1 < 5 && y1 >= 0 && y1 < 5) {
swap(p.s.st[x][y], p.s.st[x1][y1]);
if(!vis[p.s]) {
q.push(p);
vis[p.s] = true;
}
swap(p.s.st[x][y], p.s.st[x1][y1]);
}
}
}
cout << -1 << endl;
}
void solve() {
for (int i = 0; i < 5; i++) {
cin >> nx.st[i];
}
bfs();
}
当然,我也用了IDA*,方法当然还是掌握的越多越好,不过这个提交跑的好像还没有我写的A*快。
int dx[8] = { -2,-2,-1,-1,2,2,1,1 };
int dy[8] = { 1,-1,2,-2,1,-1,2,-2 };
char df[6][6] = {"11111", "01111", "00*11", "00001", "00000"};
struct mairx {
char st[6][6];
bool operator <(const mairx& t) const {
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
if(st[i][j] != t.st[i][j]) return st[i][j] > t.st[i][j];
}
}
return false;
}
};
int h(mairx x) {
int cnt = 0;
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
if(x.st[i][j] != df[i][j] && x.st[i][j] != '*') cnt++;
}
}
return cnt;
}
bool dfs(mairx s, int cnt, int cnt_e) {
int h0 = h(s);
if(h0 + cnt > cnt_e) return false; //剪枝
if(!h0) return true;
int x = -1, y;
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
if(s.st[i][j] == '*') {
x = i, y = j;
break;
}
}
if(x != -1) break;
}
for (int i = 0; i < 8; i++) {
int x1 = x + dx[i];
int y1 = y + dy[i];
if(x1 >= 0 && x1 < 5 && y1 >= 0 && y1 < 5) {
swap(s.st[x1][y1], s.st[x][y]);
if(dfs(s, cnt + 1, cnt_e)) {
return true;
}
swap(s.st[x1][y1], s.st[x][y]);
}
}
return false;
}
void solve() {
mairx nx;
for (int i = 0; i < 5; i++) {
cin >> nx.st[i];
}
for (int i = 0; i <= 15; i++) {
if(dfs(nx, 0, i)) {
cout << i << endl;
return;
}
}
cout << -1 << endl;
}