hdu1043这道八数码难题,有很多种境界解法。之前网上的博友互相抄袭的代码:离线搜索出所有状态的解,但是这种离线算法需要53MB左右的空间,显然MLE.杭电数据加强后就AC不了了。
后来在网上搜到了人工智能在线算法,强行正面搜索,解决了内存限制的问题。时间1s左右。
下面这个链接里面有八数码的八种境界。前两种境界我想到了。后面就……URL
A*算法,摘自网友。若看不懂,翻书
A* 算法的核心是公式 f = g + h。其中,g 起始状态到当前状态的距离,h 当前状态到目标状态的估计距离。
BFS 是 A* 的一种特殊状况, h 恒等于 0。
h 的估计
如果h(n)< d(n)到目标状态的实际距离,这种情况下,搜索的点数多,搜索范围大,效率低。但能得到最优解。
如果h(n)=d(n),即距离估计h(n)等于最短距离,那么搜索将严格沿着最短路径进行, 此时的搜索效率是最高的。
如果 h(n)>d(n),搜索的点数少,搜索范围小,效率高,但不能保证得到最优解。
实现起来与 BFS 很相似,要用到优先队列。
A* 算法的缺点是占用内存随问题的规模指数增长。据说 IDA* 能弥补这一点,以后再学。
本题中把状态中每个数字到正确位置的欧氏距离和作为估计值。
//虽然路径不唯一,但使用A*算法得出结果和样例一样。而且这种人工智能在线算法,即快有省时
#include <iostream>
#include <string>
#include <queue>
#include <cstring>
using namespace std;
struct State {
int f, g, h; //A*算法
int ary[9], pos;
int hash;
bool operator < (State tmp) const {
return f == tmp.f ? g > tmp.g : f > tmp.f;
} //重载小于运算符,用于优先队列
};
int fac[9] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320 };
int cantor(int ary[], int n) {
int hash = 0;
for (int i = 0; i < n; ++i) {
int k = 0;
for (int j = i + 1; j < n; ++j)
if (ary[j] < ary[i]) ++k;
hash += k * fac[n - 1 - i];
}
return hash; //不加1,hash值范围为 0:n! - 1
}
const int N = 362880, des = 0;//123456789的康拓展开值
const char op[5] = "udlr";
int dir[4][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };
bool vis[N]; int pre[N]; char ch[N];
int get_h(int ary[]) { //状态中每个数字到正确位置的欧式距离和作为估计值
int res = 0;
for (int i = 0; i < 3; ++i) //行
for (int j = 0; j < 3; ++j) //列
res += abs((ary[i*3+j] - 1) / 3 - i) + abs((ary[i*3+j] - 1) % 3 - j);
return res;
}
void print(int x) {
if (pre[x] == -1) return;
print(pre[x]);
cout << ch[x];
}
void Astar(State st) {
if (st.hash == des) { cout << endl; return; }
memset(vis, 0, sizeof vis);
priority_queue<State> pq;
pq.push(st);
vis[st.hash] = true;
pre[st.hash] = -1;
while (!pq.empty()) {
st = pq.top(); pq.pop();
int x = st.pos / 3, y = st.pos % 3;
for (int i = 0; i < 4; ++i) {
int xx = x + dir[i][0], yy = y + dir[i][1];
if (xx < 0 || xx > 2 || yy < 0 || yy > 2) continue;
State tmp = st;
tmp.pos = xx * 3 + yy;
swap(tmp.ary[st.pos], tmp.ary[tmp.pos]);
tmp.hash = cantor(tmp.ary, 9);
if (vis[tmp.hash]) continue;
pre[tmp.hash] = st.hash;
ch[tmp.hash] = op[i];
vis[tmp.hash] = true;
if (tmp.hash == des) {
print(0); cout << endl; return;
}
tmp.g++;
tmp.h = get_h(tmp.ary);
tmp.f = tmp.g + tmp.h;
pq.push(tmp);
}
}
}
int main()
{
ios::sync_with_stdio(false);
char str[9];
while (cin >> str[0]) {
for (int i = 1; i < 9; ++i)
cin >> str[i];
State s;
for (int i = 0; i < 9; ++i) {
if (str[i] == 'x') {
s.pos = i;
s.ary[i] = 9;
}
else s.ary[i] = str[i] - '0';
}
s.g = 0; s.h = get_h(s.ary);
s.f = s.g + s.h;
s.hash = cantor(s.ary, 9);
int k = 0; //通过逆序数的奇偶判断是否可解
for (int i = 0; i < 9; ++i)
for (int j = 0; j < i; ++j) //求第i个数前有几个大于ary[i]的数
if (s.ary[j] != 9 && s.ary[i] < s.ary[j])
++k; //勉强可以理解,移动一下,逆序数改变偶数值
if(k & 1) cout << "unsolvable" << endl;
else Astar(s);
}
return 0;
}
poj1077 在北大OJ,内存给了64MB,用5MB就够了
#include <iostream>
#include <string>
#include <queue>
#include <cstring>
using namespace std;
struct State {
int ary[9], pos;
int hash;
string path;
};
string path;
int fac[9] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320 };
int cantor(int ary[], int n) {
int hash = 0;
for (int i = 0; i < n; ++i) {
int k = 0;
for (int j = i + 1; j < n; ++j)
if (ary[j] < ary[i]) ++k;
hash += k * fac[n - 1 - i];
}
return hash + 1;
}
const int N = 362880 + 1, des = 46234;//123456780的康拓展开值
const string s = "udlr";
int dir[4][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };//udlr
bool vis[N];
bool bfs(State st) {
if (st.hash == des) return true;
memset(vis, 0, sizeof vis);
queue<State> q;
q.push(st);
vis[st.hash] = true;
while (!q.empty()) {
st = q.front(); q.pop();
int x = st.pos / 3, y = st.pos % 3;
for (int i = 0; i < 4; ++i) {
int xx = x + dir[i][0], yy = y + dir[i][1];
if (xx < 0 || xx > 2 || yy < 0 || yy > 2) continue;
State tmp = st;
tmp.pos = xx * 3 + yy;
tmp.ary[st.pos] = tmp.ary[tmp.pos];
tmp.ary[tmp.pos] = 0;
tmp.hash = cantor(tmp.ary, 9);
if (vis[tmp.hash]) continue;
vis[tmp.hash] = true;
tmp.path += s[i];
if (tmp.hash == des) { path = tmp.path; return true;}
q.push(tmp);
}
}
return false;
}
int main()
{
ios::sync_with_stdio(false);
string str = string(9, 0);
while (cin >> str[0]) {
for (int i = 1; i < 9; ++i)
cin >> str[i];
State start;
for (int i = 0; i < 9; ++i) {
if (str[i] == 'x') {
start.ary[i] = 0;
start.pos = i;
}
else start.ary[i] = str[i] - '0';
}
start.hash = cantor(start.ary, 9);
path = "";
if (bfs(start)) cout << path << endl;
else cout << "unsolvable" << endl;
}
return 0;
}
PS今天见到一个奇怪的操作,(自带代码混淆语言)
printf("%d%c", ary[i], " \n"[i==n]);输出数据见空格,最后一个换行,双引号返回一个指针。