一.A*算法
A*算法的核心思想就是在搜索中写一个估价函数。
普通的bfs或者dfs就是最坏的估价函数,因为估价直接等于0。
越好的A*算法,估价函数越接近。
估价函数的意思就是在计算到当前的情况下对未来的情况进行估计。
例如我跟我哥逛街,我一共有50块钱,我买了咖啡(15元),薯条(5元),汉堡(10元)各一件后后还剩下20块钱,请问我还能给我哥买几件东西,很显然,最多能买4件(全买薯条,那我哥肯定不同意。。。),最少能卖两件(1件咖啡1件薯条),那么我们的估价函数估的一定是两件。当然在这里没有什么背景条件,只可意会,我们要看具体的题目。
二.迭代加深搜索
自己的理解:就是在搜索答案时超过一个固定的答案范围就不再搜索,而搜索的答案逐渐累加,知道满足条件或者已经不需要继续搜索为止,这样可以大大的减少搜索时间。
这样的题目一般都具有固定的特点,比如说题目中明确地告诉了你超过某个答案就不再计算或者根据对题目的理解,题目的答案一定在某一个范围之内。
我们可能会需要标记这种状态是否走过,而且大多数情况下不是只用一个点就能表示的,我们需要存一张图,可以使用hash,如果时间复杂度允许,我喜欢使用sring类型来存储
这里有一道经典的例题(大家还可以去做一下埃及分数)。
题目描述(洛谷P2324)
输入格式
第一行有一个正整数 T(T≤10),表示一共有 TT 组数据。
接下来有 T 个5×5 的矩阵,0
表示白色骑士,1
表示黑色骑士,*
表示空位。两组数据之间没有空行。
输出格式
对于每组数据都输出一行。如果能在 15 步以内(包括 15 步)到达目标状态,则输出步数,否则输出 -1
。
输入输出样例
输入 #1复制
2 10110 01*11 10111 01001 00000 01011 110*1 01110 01010 00100
输出 #1复制
7 -1
说明/提示
首先我们发现了这个图是有限定的步数的,所以我们可以使用迭代加深。
#include <bits/stdc++.h>//错误原因:打了Vis标记,但是不能通过一个棋盘上的棋来确定别的棋长什么样
using namespace std ;
int n, sx, sy, dep ;
bool flag = 0 ;
int rx[10] = {1, 1, -1, -1, 2, 2, -2, -2} ;
int ry[10] = {2, -2, 2, -2, 1, -1, 1, -1} ;
char mp[7][7] ;
int goal[7][7] = {{'1', '1', '1', '1', '1'},{'0', '1', '1', '1', '1'}, {'0', '0', '*', '1', '1'}, {'0', '0', '0', '0', '1'}, {'0', '0', '0', '0', '0'}} ;
int Check(){
int def = 0 ;
for(int i = 0; i < 5; i ++){
for(int j = 0; j < 5; j ++){
if(mp[i][j] != goal[i][j]) def ++ ;
}
}
return def - 1 ;
}
void A_iddfs(int x, int y, int tot){
if(Check() + tot > dep) return ;
if(tot == dep && Check() == -1){
printf("%d\n", tot) ;
flag = 1 ;
return ;
}
for(int i = 0; i < 8; i ++){
int nx = x + rx[i], ny = y + ry[i], nc = mp[nx][ny] - '0' ;
if(nx < 0 || nx >= 5 || ny < 0 || ny >= 5) continue ;
swap(mp[x][y], mp[nx][ny]) ;
A_iddfs(nx, ny, tot + 1) ;
swap(mp[x][y], mp[nx][ny]) ;
if(flag == 1) return ;
}
return ;
}
int main()
{
scanf("%d", &n) ;
while(n --){
flag = 0 ;
for(int i = 0; i < 5; i ++)
{
scanf("%s", mp[i]) ;
for(int j = 0; j < 5; j ++){
if(mp[i][j] == '*') sx = i, sy = j ;
}
}
for(dep = 1; dep <= 15; dep ++){
A_iddfs(sx, sy, 0) ;
if(flag) break ;
}
if(!flag) printf("-1\n") ;
}
}
三.双向bfs、dfs搜索
如果用双向dfs和bfs搜索的话,我们的时间可以减少一半,原因是送两个点往外画圈圈相交的面积比一个点往外画圈圈相交的面积更少呀。(可以画图滴)
在搜索中,搜索结果是固定的,那么我们就可以用双向搜索,同时我们也可以结合迭代加深(搜索中的方法是可以套在一起的)。
注意的是我们在拓展点的时候,是只使用一个队列的,我们只能一圈一圈的拓展,不可以两个状态中一个点一个点交替着拓展,这样会出现问题(想知道的话就来cqbzhf的gm的课件上找答案啊)
#include <bits/stdc++.h>
using namespace std ;
struct Node{
int x, y, f, num ;
string mp ;
}s, e ;
int rx[10] = {1, 1, -1, -1, 2, 2, -2, -2} ;
int ry[10] = {2, -2, 2, -2, 1, -1, 1, -1} ;
int n ;
map<string, bool> Vis[2] ;
map<string, int> ste[2] ;
bool out(int x, int y){
if(x < 0 || x >= 5 || y < 0 || y >= 5) return false ;
return true ;
}
char str ;
void bbfs()
{
queue<Node> q ;
Vis[s.f][s.mp] = Vis[e.f][e.mp] = 1 ;
q.push(s) ;
q.push(e) ;
Node a, b ;
int S = 0 ;
while(q.empty() == 0){
a = q.front() ;
q.pop() ;
if(Vis[!a.f][a.mp]){
S = ste[1][a.mp] + ste[0][a.mp] ;
if(S <= 15) printf("%d\n", S) ;
else printf("-1\n") ;
return ;
}
b = a, b.num = a.num + 1 ;
for(int i = 0; i < 8; i ++){
int x = a.x + rx[i], y = a.y + ry[i] ;
if(out(x, y) == false) continue ;
b.x = x, b.y = y ;
int id1 = x * 5 + y, id2 = a.x * 5 + a.y ;
swap(b.mp[id1], b.mp[id2]) ;
if(!Vis[b.f][b.mp]){
if(Vis[!b.f][b.mp]){
ste[b.f][b.mp] = b.num ;
S = ste[1][b.mp] + ste[0][b.mp] ;
//printf("%d %d\n", ste[1][b.mp] , ste[0][b.mp]) ;
if(S <= 15) printf("%d\n", S) ;
else printf("-1\n") ;
return ;
}
Vis[b.f][b.mp] = 1 ;
if(b.num <= 8){//错因,b.num
ste[b.f][b.mp] = b.num ;
q.push(b) ;
}
}
swap(b.mp[id1], b.mp[id2]) ;
}
}
printf("-1\n") ;
return ;
}
int main()
{
scanf("%d", &n) ;
e.num = 0, e.f = 1, e.x = 2, e.y = 2 ;
e.mp = "1111101111002110000100000" ;
getchar() ;
while(n --){
Vis[1].clear(), Vis[0].clear() ;
ste[1].clear(), ste[0].clear() ;
s.mp = "", s.num = 0, s.f = 0 ;
for(int i = 0; i < 5; i ++){
for(int j = 0; j < 5; j ++){
str = getchar() ;
if(str == '*'){
s.mp += '2' ;
s.x = i, s.y = j ;
}
else s.mp += str ;
}
getchar() ;
}
bbfs() ;
}
}
明天要考试,复习一下啦
谢谢观看<(* ̄▽ ̄*)/