A*算法网上有很多资料,在这里不想写…
最主要的是估价函数f,估的是当前状态到最终状态的代价。
设 f′ 为真正的代价
f<f′ ,能搜到最优解,但是复杂度不优。
f=′f′ ,能搜到最优解,且复杂度最优。
f>f′ ,有可能搜不到最优解,但是跑得快。
其核心在于预处理出所有点到终点的最短路h。
设g为起点到这个点的长度。
f = g +h
按f的大小从小到大取,用堆维护。
每次取出来就将它相邻的点更新。
这样的正确性你可以想象是把小于(和一部分等于)第k短路长度的路径全部找了出来,复杂度取决于这个再乘上数据结构的复杂度。
Code:
#include
#include
#include
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;
const int N = 1e4 + 5, M = 2e5 + 5;
int n, m, x, y, z, s, t, k;
struct edge {
int tt, next[M], to[M], final[N], w[M];
void link(int x, int y, int z) {next[++ tt] = final[x], to[tt] = y, w[tt] = z, final[x] = tt;}
} e1, e2;
int d[M * 50], dis[N], bz[N];
void Spfa() {
memset(dis, 127, sizeof dis);
dis[t] = 0; d[1] = t; d[0] = 1; bz[t] = 1;
fo(i, 1, d[0]) {
int x = d[i];
for(int j = e2.final[x]; j; j = e2.next[j]) {
int y = e2.to[j], z = e2.w[j];
if(dis[x] + z < dis[y]) {
dis[y] = dis[x] + z;
if(!bz[y]) bz[y] = 1, d[++ d[0]] = y;
}
}
bz[x] = 0;
}
}
struct node {
int x, g, h;
node (int _x, int _g, int _h) {x = _x, g = _g, h = _h;}
node () {}
};
bool operator <(node a, node b) {return a.g + a.h < b.g + b.h;}
int tot, ts, ans;
multiset st;
int main() {
scanf("%d %d %d", &n, &m, &k);
fo(i, 1, m) {
scanf("%d %d %d", &x, &y, &z);
e1.link(x, y, z); e2.link(y, x, z);
}
s = 1; t = n;
Spfa();
st.insert(node(s, 0, dis[s]));
while(1) {
ts ++;
node p = *st.begin();
int x = p.x;
if(x == t) {
if(p.g > ans) tot ++, ans = p.g;
}
if(tot == k) {
printf("%d", p.g); return 0;
}
st.erase(st.begin());
for(int j = e1.final[x]; j; j = e1.next[j]) {
int y = e1.to[j], z = e1.w[j];
st.insert(node(y, p.g + z, dis[y]));
}
}
}
链接.
A*裸题。
双向广搜应该是可以过的,但是状态的压缩有些麻烦。
深搜的话加个剪枝就行了。
设当前状态已有步数为g,和目标状态的不同数为h, 则至少还需要h-1步才能到。
因此如果有g+h-1>已知的ans,退了就行了。
Code:
#include
#include
#include
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;
int T, ans;
int move[8][2] = {{1, 2}, {-1, 2}, {1, -2}, {-1, -2}, {2, 1}, {-2, 1}, {2, -1}, {-2, -1}};
char s[6][6], s2[6][6];
void dg(int g) {
int sum = 0, x, y;
fo(i, 1, 5) fo(j, 1, 5) {
sum += s[i][j] != s2[i][j];
if(s[i][j] == '*') x = i, y = j;
}
if(g + sum > ans) return;
if(!sum) ans = g;
fo(i, 0, 7) {
int l = x + move[i][0], r = y + move[i][1];
if(l > 0 && l <= 5 && r > 0 && r <= 5) {
swap(s[l][r], s[x][y]);
dg(g + 1);
swap(s[l][r], s[x][y]);
}
}
}
int main() {
freopen("a.in", "r", stdin);
fo(i, 1, 5) s2[1][i] = '1';
s2[2][1] = '0'; fo(i, 2, 5) s2[2][i] = '1';
s2[3][1] = s2[3][2] = '0'; s2[3][3] = '*'; s2[3][4] = s2[3][5] = '1';
s2[4][5] = '1'; fo(i, 1, 4) s2[4][i] = '0';
fo(i, 1, 5) s2[5][i] = '0';
for(scanf("%d", &T); T; T --) {
fo(i, 1, 5) scanf("%s", s[i] + 1);
ans = 16;
dg(0);
if(ans == 16) printf("-1\n"); else printf("%d\n", ans);
}
}