kuangbin 专题二 搜索进阶
题目详单见 [kuangbin带你飞]专题1-23。
算法介绍见 搜索部分简介 - OI Wiki。
HDU-1043 Eight
用康托展开将状态映射成排列数的字典序下标,然后
- 用 BFS 预处理初始状态(123456789)到其他所有状态的路径,
- 或者结合数学上八数码问题是否有解的判别方法和 A* 算法(有解时)。
1 |
|
1 |
|
HDU-3567 Eight II
跟上一题相比,测试用例变多,每个用例指定了起始点和终点,对应的还是可以
- 预处理九种基本的起始点,再将每个用例的起始点映射成基本起始点中的一个,再按照同样的映射规则将终点作映射,最后跟上一题一样用 BFS 搜索并记录方案。
- 或者,因为题目确保起始点能到达终点,所以对每个用例都可以直接用 IDA* 算法搜索路径。
1 |
|
1 |
|
HDU-2181 哈密顿绕行世界问题
按字典序将路径排序再 DFS 即可。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
using namespace std;
int g[21][3], m, path[21], ans_cnt;
bool st[21];
void dfs(int u, int path_idx)
{
st[u] = 1;
path[path_idx] = u;
for (int i = 0; i < 3; ++i)
{
int v = g[u][i];
if (!st[v])
dfs(v, path_idx + 1);
else if (v == m && path_idx == 20)
{
cout << ++ans_cnt << ": ";
for (int j = 1; j <= 20; ++j)
cout << " " << path[j];
cout << " " << m << endl;
}
}
st[u] = 0;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
for (int i = 1; i <= 20; ++i)
{
cin >> g[i][0] >> g[i][1] >> g[i][2];
sort(g[i], g[i] + 3);
}
while (cin >> m, m)
{
ans_cnt = 0;
memset(st, 0, sizeof st);
dfs(m, 1);
}
return 0;
}
HDU-3533 Escape
预处理各个位置在某一时刻是否有子弹,再使用 BFS 或者 A* (注:因为不一定有解,所以 A* 时间复杂度可能很高) 算法寻找逃跑路径。
1 |
|
1 |
|
HDU-1560 DNA sequence
这题如果单纯用 DFS ,搜索空间比较大,会 TLE。可以用 IDA* 优化搜索过程,先将最长序列的长度作为初始搜索深度,再配合启发函数加速搜索。启发函数可以是,
- 序列中未匹配部分的最长长度,
- 或者叠加序列中未匹配的 ACGT 长度(即,至少仍需多少个 A + 至少仍需多少个 C + …)。
1 |
|
1 |
|
1 |
|
ZOJ-2477 Magic Cube
题目限定五步内还原三阶魔方,可以使用 IDA* 搜索。因为每个面的中心颜色是不变的,即目标状态是确定的,又因为每次旋转最多更改同一面三个位置的颜色,所以可以找出与中心颜色差最多的一面,取(最大差值+2)/3
为估价函数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
using namespace std;
/*
0 1 2
3 4 5
6 7 8
9 10 11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30 31 32
33 34 35 36 37 38 39 40 41 42 43 44
45 46 47
48 49 50
51 52 53
*/
// indexes of each points (without centre) in each face.
int idx[6][8] = {
{0, 1, 2, 3, 5, 6, 7, 8}, //
{9, 10, 11, 21, 23, 33, 34, 35}, //
{12, 13, 14, 24, 26, 36, 37, 38}, //
{15, 16, 17, 27, 29, 39, 40, 41}, //
{18, 19, 20, 30, 32, 42, 43, 44}, //
{45, 46, 47, 48, 50, 51, 52, 53}};
int centre[6] = {4, 22, 25, 28, 31, 49}; // centre indexes.
// there are 12 = (clock-wise + counter clock-wisk) * (6 faces)
// kinds of rotation. For each rotation, centre remains, the other
// 20 = (8 cubes) + (3 cubes) * (4 faces) cubes get rotated.
int rotation[12][20] = {
// face 0.
// 11 <=> 9, 23 <=> 10, ... , 44 <=> 45.
{11, 23, 35, 34, 33, 21, 9, 10, 51, 48, 45, 36, 24, 12, 6, 3, 0, 20, 32, 44}, // direction 1.
{9, 10, 11, 23, 35, 34, 33, 21, 36, 24, 12, 6, 3, 0, 20, 32, 44, 51, 48, 45}, // direction -1.
// face 1.
// 14 <=> 12, 13 <=> 24, ... , 35 <=> 47.
{14, 13, 26, 38, 37, 36, 24, 12, 45, 46, 47, 39, 27, 15, 8, 7, 6, 11, 23, 35}, // direction 1.
{12, 24, 13, 14, 26, 38, 37, 36, 39, 27, 15, 8, 7, 6, 11, 23, 35, 45, 46, 47}, // direction -1.
// ...
{17, 29, 41, 40, 39, 27, 15, 16, 47, 50, 53, 42, 30, 18, 2, 5, 8, 14, 26, 38}, // ...
{15, 16, 17, 29, 41, 40, 39, 27, 42, 30, 18, 2, 5, 8, 14, 26, 38, 47, 50, 53},
{18, 19, 20, 32, 44, 43, 42, 30, 53, 52, 51, 33, 21, 9, 0, 1, 2, 17, 29, 41},
{42, 30, 18, 19, 20, 32, 44, 43, 33, 21, 9, 0, 1, 2, 17, 29, 41, 53, 52, 51},
{0, 1, 2, 5, 8, 7, 6, 3, 12, 13, 14, 15, 16, 17, 18, 19, 20, 9, 10, 11},
{6, 3, 0, 1, 2, 5, 8, 7, 15, 16, 17, 18, 19, 20, 9, 10, 11, 12, 13, 14},
{45, 46, 47, 50, 53, 52, 51, 48, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33},
{51, 48, 45, 46, 47, 50, 53, 52, 41, 40, 39, 38, 37, 36, 35, 34, 33, 44, 43, 42}};
int t, depth, ans[6];
char ch, a[54];
char get_in()
{
char ch;
while (1)
{
ch = getchar();
if (ch >= 'a' && ch <= 'z')
return ch;
}
}
char f(char *maze)
{
int max_diff = 0;
for (int i = 0; i < 6; ++i)
{
int diff = 0;
for (int j = 0; j < 8; ++j)
if (maze[idx[i][j]] != maze[centre[i]])
++diff;
max_diff = max(max_diff, diff);
}
// by 1 rotation,
// we can swith color of 3 (at most) cubes that are in the same face.
return (max_diff + 2) / 3;
}
bool dfs(int u, char *a, int pre)
{
if (u + f(a) > depth)
return 0;
if (u == depth)
return 1;
for (int i = 0; i < 12; ++i)
{
if ((i ^ pre) == 1) // rotate back?
continue; // no!
char b[54];
memcpy(b, a, 54 * sizeof(char));
ans[depth - u] = i;
for (int j = 0; j < 20; ++j)
b[rotation[i][j]] = a[rotation[i ^ 1][j]];
if (dfs(u + 1, b, i))
return 1;
}
return 0;
}
int main()
{
scanf("%d", &t);
while (t--)
{
for (int i = 0; i < 54; ++i)
a[i] = get_in();
depth = f(a);
if (depth == 0)
{
printf("0\n");
continue;
}
for (; depth <= 5; ++depth)
if (dfs(0, a, -1))
{
printf("%d\n", depth);
for (int j = depth; j > 0; --j)
printf("%d %d%c", ans[j] / 2, (ans[j] & 1) ? -1 : 1, j > 1 ? ' ' : '\n');
break;
}
if (depth > 5)
printf("-1\n");
}
return 0;
}
HDU-3085 Nightmare Ⅱ
双向 BFS,M/G 走,用与 Z 的距离判定是否会被抓到,只要双方都能走到一个不被抓到的点即成功。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
using namespace std;
const int N = 800;
int t, n, m, z, zs[2][2], step, ds[] = {1, 0, -1, 0, 1};
char g[N][N];
queue<pair<int, int>> q[2];
bool st[2][N][N];
bool catched(int x, int y, int step)
{
return abs(x - zs[0][0]) + abs(y - zs[0][1]) <= 2 * step || //
abs(x - zs[1][0]) + abs(y - zs[1][1]) <= 2 * step;
}
bool bfs(int p)
{
int sz = q[p].size();
while (sz--)
{
auto [x, y] = q[p].front();
q[p].pop();
if (catched(x, y, step))
continue;
for (int i = 0; i < 4; ++i)
{
int nx = x + ds[i], ny = y + ds[i + 1];
if (nx < 0 || nx >= n || ny < 0 || ny >= m || //
g[nx][ny] == 'X' || st[p][nx][ny] || catched(nx, ny, step))
continue;
st[p][nx][ny] = 1;
if (st[0][nx][ny] && st[1][nx][ny])
return 1;
q[p].emplace(nx, ny);
}
}
return 0;
}
int solve()
{
step = 0;
while (q[0].size() || q[1].size())
{
++step;
if (bfs(0))
return step;
if (bfs(0))
return step;
if (bfs(0))
return step;
if (bfs(1))
return step;
}
return -1;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
cin >> t;
while (t--)
{
z = 0;
memset(st, 0, sizeof st);
while (q[0].size())
q[0].pop();
while (q[1].size())
q[1].pop();
cin >> n >> m;
for (int i = 0; i < n; ++i)
{
cin >> g[i];
for (int j = 0; j < m; ++j)
{
if (g[i][j] == 'Z')
{
zs[z][0] = i, zs[z][1] = j;
z++;
}
else if (g[i][j] == 'M')
q[0].emplace(i, j), st[0][i][j] = 1;
else if (g[i][j] == 'G')
q[1].emplace(i, j), st[1][i][j] = 1;
}
}
cout << solve() << endl;
}
return 0;
}
HDU-1067 Gap
一眼 BFS,关键是状态表示。可以将数字当作 ASCII 码,转成对应字符,连接成串就是当前状态。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
using namespace std;
int t;
string start(32, 1), target(32, 1);
void moveChar(string &str, char ch, int new_idx)
{
int idx = 0;
while (idx < 32 && str[idx] != ch)
++idx;
if (idx != 32)
{
str[new_idx] = ch;
str[idx] = 1;
}
}
int bfs()
{
queue<string> q;
q.push(start);
unordered_set<string> hash;
hash.insert(start);
int step = 0;
while (q.size())
{
int sz = q.size();
while (sz--)
{
string u = q.front();
q.pop();
if (u == target)
return step;
for (int i = 0; i < 4; ++i)
for (int j = 1; j < 8; ++j)
{
if (u[i * 8 + j] == 1 && u[i * 8 + j - 1] != 1)
{
string v = u;
moveChar(v, u[i * 8 + j - 1] + 1, i * 8 + j);
if (hash.find(v) == hash.end())
{
hash.insert(v);
q.push(v);
}
}
}
}
++step;
}
return -1;
}
int main()
{
for (int i = 0; i < 4; ++i)
for (int j = 0; j < 7; ++j)
target[i * 8 + j] = (i + 1) * 10 + (j + 1);
scanf("%d", &t);
start = string(32, 1);
while (t--)
{
for (int i = 0; i < 4; ++i)
for (int j = 1, x; j < 8; ++j)
{
scanf("%d", &x);
if (x % 10 == 1)
start[(x / 10 - 1) * 8] = x, start[i * 8 + j] = 1;
else
start[i * 8 + j] = x;
}
printf("%d\n", bfs());
}
return 0;
}
HDU-2102 A计划
1 |
|
HDU-3001 Travelling
1 |
|
1 |
|