首页 > 编程语言 >算法基础-搜索

算法基础-搜索

时间:2022-11-30 23:11:25浏览次数:60  
标签:return int father 基础 算法 搜索 new include dis

搜索

框架

dfs框架

#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
struct Node {
    char value;   int lson, rson;
} tree[N];                     //tree[0]不用,0表示空结点
int index = 1;                 //记录结点存在tree[]的位置,从tree[1]开始用
int newNode(char val) {        //新建结点
    tree[index].value = val;
    tree[index].lson = 0;     //0表示空,tree[0]不用
    tree[index].rson = 0;
    return index ++;
}
void insert(int &father, int child, int l_r) { //插入孩子
    if (l_r == 0) tree[father].lson = child;  //左孩子
    else         tree[father].rson = child;   //右孩子
}
int dfn[N] = {0};              //dfn[i]是结点i的时间戳
int dfn_timer = 0;
void dfn_order (int father) {
    if (father != 0) {
        dfn[father] = ++dfn_timer;
        printf("dfn[%c]=%d; ", tree[father].value, dfn[father]);//打印时间戳
        dfn_order (tree[father].lson);
        dfn_order (tree[father].rson);
    }
}
int visit_timer = 0;
void visit_order (int father) {       //打印DFS序
    if (father != 0) {
        printf("visit[%c]=%d; ", tree[father].value, ++visit_timer);
        //打印DFS序:第1次访问结点
        visit_order (tree[father].lson);
        visit_order (tree[father].rson);
        printf("visit[%c]=%d; ", tree[father].value, ++visit_timer);
        //打印DFS序:第2次回溯
    }
}
int deep[N] = {0};                    //deep[i]是结点i的深度
int deep_timer = 0;
void deep_node (int father) {
    if (father != 0) {
        deep[father] = ++deep_timer;  //打印树的深度,第一次访问时,深度+1
        printf("deep[%c]=%d; ", tree[father].value, deep[father]);
        deep_node (tree[father].lson);
        deep_node (tree[father].rson);
        deep_timer--;                 //回溯时,深度-1
    }
}
int num[N] = {0};        //num[i]是以i为父亲的子树上的结点总数
int num_node (int father) {
    if (father == 0)  return 0;
    else {
        num[father] = num_node (tree[father].lson) +
                      num_node (tree[father].rson) + 1;
        printf("num[%c]=%d; ", tree[father].value, num[father]); //打印数量
        return num[father];
    }
}
void preorder (int father) {                //求先序序列
    if (father != 0) {
        cout << tree[father].value << " ";  //先序输出
        preorder (tree[father].lson);
        preorder (tree[father].rson);
    }
}
void inorder (int father) {                  //求中序序列
    if (father != 0) {
        inorder (tree[father].lson);
        cout << tree[father].value << " ";   //中序输出
        inorder (tree[father].rson);
    }
}
void postorder (int father) {                //求后序序列
    if (father != 0) {
        postorder (tree[father].lson);
        postorder (tree[father].rson);
        cout << tree[father].value << " ";   //后序输出
    }
}
int buildtree() {                            //建一棵树
    int A = newNode('A'); int B = newNode('B'); int C = newNode('C'); //定义结点
    int D = newNode('D'); int E = newNode('E'); int F = newNode('F');
    int G = newNode('G'); int H = newNode('H'); int I = newNode('I');
    insert(E, B, 0);  insert(E, G, 1);      //建树。E的左孩子是B,右孩子是G
    insert(B, A, 0);  insert(B, D, 1);  insert(G, F, 0);  insert(G, I, 1);
    insert(D, C, 0);  insert(I, H, 0);
    int root = E;
    return root;
}
int main() {
    int root = buildtree();
    cout << "dfn order: ";     dfn_order(root); cout << endl;   //打印时间戳
    cout << "visit order: "; visit_order(root); cout << endl;   //打印DFS序
    cout << "deep order: ";    deep_node(root); cout << endl;   //打印结点深度
    cout << "num of tree: ";    num_node(root); cout << endl;   //打印子树上的结点数
    cout << "in order:   ";      inorder(root); cout << endl;   //打印中序序列
    cout << "pre order:  ";     preorder(root); cout << endl;   //打印先序序列
    cout << "post order: ";    postorder(root); cout << endl;   //打印后序序列
    return 0;
}
/*   输出是:
dfn order: dfn[E]=1; dfn[B]=2; dfn[A]=3; dfn[D]=4; dfn[C]=5; dfn[G]=6; dfn[F]=7; dfn[I]=8; dfn[H]=9;
visit order: visit[E]=1; visit[B]=2; visit[A]=3; visit[A]=4; visit[D]=5; visit[C]=6; visit[C]=7; visit[D]=8; visit[B]=9; visit[G]=10; visit[F]=11; visit[F]=12; visit[I]=13; visit[H]=14; visit[H]=15; visit[I]=16; visit[G]=17; visit[E]=18;
deep order: deep[E]=1; deep[B]=2; deep[A]=3; deep[D]=3; deep[C]=4; deep[G]=2; deep[F]=3; deep[I]=3; deep[H]=4;
num of tree: num[A]=1; num[C]=1; num[D]=2; num[B]=4; num[F]=1; num[H]=1; num[I]=2; num[G]=4; num[E]=9;
in order:   A B C D E F G H I
pre order:  E B A D C G F I H
post order: A C D B F H I G E    */

bfs框架

#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
struct Node {                 //用静态数组记录二叉树
    char value;
    int lson, rson;           //左右孩子
} tree[N];                    //tree[0]不用,0表示空结点
int index = 1;                //记录结点存在tree[]的位置,从tree[1]开始用
int newNode(char val) {
    tree[index].value = val;
    tree[index].lson = 0;     //0表示空,tree[0]不用
    tree[index].rson = 0;
    return index ++;
}
void Insert(int &father, int child, int l_r) {   //插入孩子
    if (l_r == 0)  tree[father].lson = child;    //左孩子
    else          tree[father].rson = child;     //右孩子
}
int buildtree() {             //建一棵二叉树
    int A = newNode('A'); int B = newNode('B'); int C = newNode('C');
    int D = newNode('D'); int E = newNode('E'); int F = newNode('F');
    int G = newNode('G'); int H = newNode('H'); int I = newNode('I');
    Insert(E, B, 0);  Insert(E, G, 1);   //E的左孩子是B,右孩子是G
    Insert(B, A, 0);  Insert(B, D, 1);
    Insert(G, F, 0);  Insert(G, I, 1);
    Insert(D, C, 0);  Insert(I, H, 0);
    int root = E;
    return root;
}
int main() {
    int root = buildtree();
    queue <int> q;
    q.push(root);                          //从根结点开始
    while (q.size()) {
        int tmp = q.front();
        cout << tree[tmp].value << " ";    //打印队头
        q.pop();                           //去掉队头
        if (tree[tmp].lson != 0) q.push(tree[tmp].lson);  //左孩子入队
        if (tree[tmp].rson != 0) q.push(tree[tmp].rson);  //右孩子入队
    }
    return 0;
}

用途

剪枝 记忆化dfs 双向bfs 迭代加dfs A - starbfs IDAdfs

连通性问题

以蓝桥《全球变暖问题》

dfs
const int N;
int mp[N][N];
int vis[N][N];
int d[4][2] = {{0, 1}, {0, -1}, {1, 0}, { -1, 0}};
int flag;
void dfs(int x, int y) {
    vis[x][y] = 1;
    if ( mp[x][y + 1] == '#' && mp[x][y - 1] == '#' &&
            mp[x + 1][y] == '#' && mp[x - 1][y] == '#'   )
        flag = 1;//有一次能把flag变成一就证明不会被淹没
    for (int i = 0; i < 4; i++) {
        int mx = x + d[i][0], my = y + d[i][1];
        if (vis[mx][my] == 0 && mp[mx][my] == '#') //注意为什么要判断vis[][]
            //继续DFS未搜过的陆地,目的是标记它们
            dfs(mx, my);//这个#周围所有的#
    }
}
int main() {
    int n;    cin >> n;
    for (int i = 0; i < n; i++)   cin >> mp[i];
    int ans = 0 ;
    for (int i = 1; i <= n; i++)        //DFS所有像素点
        for (int j = 1; j <= n; j++)
            if (mp[i][j] == '#' && vis[i][j] == 0) {//vis[][]会排除已经搜过的点记忆化的感觉
                flag = 0;               //假设这个岛被淹
                dfs(i, j);              //找这个岛中有没有高地,如果有,置flag=1
                if (flag == 0)  ans++;  //这个岛被淹了,统计被淹没岛的数量
            }
    cout << ans << endl;
    return 0;
}
bfs
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
char mp[N][N];
int vis[N][N];
int d[4][2] = {{0, 1}, {0, -1}, {1, 0}, { -1, 0}}; //四个方向
int flag;
void bfs(int x, int y) {
    queue<pair<int, int>> q;
    q.push({x, y});
    vis[x][y] = 1;      //标记这个'#'被搜过
    while (q.size()) {
        pair<int, int> t = q.front();
        q.pop();
        int tx = t.first, ty = t.second;
        if ( mp[tx][ty + 1] == '#' && mp[tx][ty - 1] == '#' &&
                mp[tx + 1][ty] == '#' && mp[tx - 1][ty] == '#'   )
            flag = 1; //上下左右都是陆地,不会淹没
        for (int i = 0; i < 4; i++) {    //扩展(tx,ty)的4个邻居
            int nx = tx + d[i][0], ny = ty + d[i][1];
            if (vis[nx][ny] == 0 && mp[nx][ny] == '#') { //把陆地放进队列
                vis[nx][ny] = 1;  //注意:这一句必不可少
                q.push({nx, ny});
            }
        }
    }
}
int main() {
    int n;  cin >> n;
    for (int i = 0; i < n; i++)    cin >> mp[i];
    int ans = 0;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            if (mp[i][j] == '#' && vis[i][j] == 0) {
                flag = 0;
                bfs(i, j);
                if (flag == 0) ans++; //这个岛全部被淹,统计岛的数量
            }
    cout << ans << endl;
    return 0;
}

岛屿数量问题

image-20221111210908267

dfs
class Solution {
    boolean vis[][];//可以原数组经过之后写成0也可以写一个vis
    int res=0;
    int len1,len2;
    int add[][]=new int[][]{{1,0},{-1,0},{0,1},{0,-1}};//别和cpp的声明方法弄混了
    public int numIslands(char[][] grid) {
        len1=grid.length;
        len2=grid[0].length;//注意是长度[0].length
        vis=new boolean[len1][len2];//成员变量初始化
         for(int i=0;i<len1;++i)
            for(int j=0;j<len2;++j){
                if(grid[i][j]=='1'&&!vis[i][j]){//vis[]过的'1'不再加
                    res+=1;
                 System.out.println(i+" "+j);//test一下
                    dfs(grid,i,j);//从一个新岛屿开始搜索
                    
                }
            }
        return res;
    }
    void dfs(char mp[][],int x,int y){
        if(uncheck(mp,x,y))return;//在数组之外直接排除
        vis[x][y]=true;//没有别排除且已经被搜索vis一下
        for(int i=0;i<4;i++){//四个方向搜索一下
           int nx=x+add[i][0],ny=y+add[i][1];//新坐标,最好nx ny方便观察,但一定注意后面是不是应该用nx ny不能多用也不能少用
            if(!uncheck(mp,nx,ny)&&mp[nx][ny]=='1'&&!vis[nx][ny]){//数组内 是陆地1 没有搜索过 
            
            dfs(mp,nx,ny);
        }
        }
    }
    boolean uncheck(char mp[][],int x,int y){//判断是否数组内
        return x<0||y<0||x>=len1||y>=len2;
    }
}
bfs
class Solution {
    static boolean vis[][];
    static Queue<int[]>q;//Queue  最常见用LinkedList实现
    static int add[][]=new int[][]{{1,0},{-1,0},{0,1},{0,-1}};//这种写法一定要注意逗号
    static int len1,len2;
    public int numIslands(char[][] grid) {
        q=new LinkedList<>();
        len1=grid.length;
        len2=grid[0].length;
        int res=0;
        vis=new boolean[len1][len2];
        for(int i=0;i<len1;i++)
            for(int j=0;j<len2;j++){
                if(grid[i][j]=='1'&&!vis[i][j]){
                 q.offer(new int[]{i,j});
                 vis[i][j]=true;//一进队列直接标注
                    bfs(grid,i,j);
                    System.out.println(i+" "+j);
                    res++;
                }
            }
            return res;
    }
    void bfs(char mp[][],int x,int y){
        while(!q.isEmpty()){
            int[]tmp=q.poll();//与cpp不同他会直接蹦出来
            x=tmp[0];y=tmp[1];
            for(int i=0;i<4;i++){
            int nx=x+add[i][0],ny=y+add[i][1];

            if(check(mp,nx,ny)&&!vis[nx][ny]&&mp[nx][ny]=='1'){
                vis[nx][ny]=true;
                q.offer(new int[]{nx,ny});
            }
            }
        }
        
    }
    boolean check(char[][] mp,int nx,int ny){
        return nx>=0&&ny>=0&&nx<len1&&ny<len2;
    }
}

岛屿周长问题

dfs
class Solution {
    static int len1,len2;
    static boolean vis[][];
    static int add[][]=new int[][]{{0,1},{0,-1},{1,0},{-1,0}};
    int res=0;//这个不能设置成static
    public int islandPerimeter(int[][] grid) {
        len1=grid.length;
        len2=grid[0].length;
        vis=new boolean[len1][len2];
        for(int i=0;i<len1;i++){
            for(int j=0;j<len2;j++){
                if(!vis[i][j]&&grid[i][j]==1){
                    vis[i][j]=true;
                    dfs(grid,i,j);
                }
            }
        }
        return res;
    }
    void dfs(int mp[][],int x,int y){
        
        
        for(int i=0;i<4;i++){
            int nx=x+add[i][0];int ny=y+add[i][1];
            if(!check(nx,ny)){
                System.out.println(nx+" "+ny);//测试一下
                res++;continue;}//遇到边界周长加一
            if(mp[nx][ny]==0){res++;continue;}//遇到0周长加一
            if(!vis[nx][ny]){
                vis[nx][ny]=true;
                dfs(mp,nx,ny); 
            }
                           
        }
    }
    boolean check(int x,int y){
        return x>=0&&y>=0&&x<len1&&y<len2;
    }
}

剪枝

bfs判重

蓝桥《跳蚱蜢》八数码问题

012345678—— > 087654321
每层扩展就是蚱蜢跳一次
每次四种跳法(隔 * 2 + 不隔 * 2)判重之后只有9!

#include<bits/stdc++.h>
using namespace std;
struct node {
    node() {}
    node(string ss, int tt) {s = ss, t = tt;}
    string s;
    int t;
};
//(1) map
map<string, bool> mp;
//(2) set
// set<string> visited;    //记录已经搜索过的状态
queue<node> q;
void solve() {
    while (!q.empty()) {
        node now = q.front();
        q.pop();
        string s = now.s;
        int step = now.t;
        if (s == "087654321") { cout << step << endl; break;} //到目标了,输出跳跃步数
        int i;
        for (i = 0 ; i < 10 ; i++)              //找到盘子的位置i
            if (s[i] == '0')  break;
        for (int j = i - 2 ; j <= i + 2 ; j++) { //4种跳法
            int k = (j + 9) % 9;
            if (k == i)  continue;              //这是当前状态,不用检查
            string news = s;
            char tmp = news[i];
            news[i] = news[k];
            news[k] = tmp;  //跳到一种情况
//(1) map
            if (!mp[news]) {               //判重:这个情况没有出现过
                mp[news] = true;
                q.push(node(news, step + 1));
            }
//(2)set
            /*          if(visited.count(news)==0){    //判重:这个情况没有出现过
                            visited.insert(news);
                            q.push(node(news, step + 1));
                        }      */
        }
    }
}
int main() {
    string s = "012345678";
    q.push(node(s, 0));
//(1) map
    mp[s] = true;
    solve();
    return 0;
}

过会补充Java写法


洪水填充

bfs与最短路

lanqiao的迷宫
#include<bits/stdc++.h>
using namespace std;
struct node {
    int x;
    int y;
//(1)简单方法:
    string path;  //path,记录从起点(0,0)到这个点(x,y)的完整路径
};
char mp[31][51];  //存地图
char k[4] = {'D', 'L', 'R', 'U'}; //字典序
int dir[4][2] = {{1, 0}, {0, -1}, {0, 1}, { -1, 0}};
int vis[30][50];  //标记。vis=1: 已经搜过,不用再搜

//(2)标准方法:
char pre[31][51];        //   用于查找前驱点。例如pre[x][y] = ‘D’,表示上一个点
//往下走一步到了(x,y),那么上一个点是(x-1,y)
void print_path(int x, int y) {    //打印路径:从(0,0)到(29,49)
    if (x == 0 && y == 0)    return; //回溯到了起点,递归结束,返回
    if (pre[x][y] == 'D')  print_path(x - 1, y); //回溯,往上 U
    if (pre[x][y] == 'L')  print_path(x,  y + 1); //回溯,往右 R
    if (pre[x][y] == 'R')  print_path(x,  y - 1);
    if (pre[x][y] == 'U')  print_path(x + 1, y);
    printf("%c", pre[x][y]);                 //最后打印的是终点
}
void bfs() {
    node start; start.x = 0;  start.y = 0;
//(1)简单方法:
    start.path = "";
    vis[0][0] = 1;             //标记起点被搜过
    queue<node>q;
    q.push(start);             //把第一个点放进队列,开始BFS
    while (!q.empty()) {
        node now = q.front();  //取出队首
        q.pop();
        if (now.x == 29 && now.y == 49) { //第一次达到终点,这就是字典序最小的最短路径
//(1)简单方法:打印完整路径
            cout << now.path << endl;
//(2)标准方法:打印完整路径,从终点回溯到起点,打印出来是从起点到终点的正序
            print_path(29, 49);
            return;
        }
        for (int i = 0; i < 4; i++) { //扩散邻居结点
            node next;
            next.x = now.x + dir[i][0];  next.y = now.y + dir[i][1];
            if (next.x < 0 || next.x >= 30 || next.y < 0 || next.y >= 50) //越界了
                continue;
            if (vis[next.x][next.y] == 1 || mp[next.x][next.y] == '1')
                continue;           //vis=1:已经搜过;  mp=1:是障碍
            vis[next.x][next.y] = 1; //标记被搜过
//(1)简单方法:记录完整路径:复制上一个点的路径,加上这一步
            next.path = now.path + k[i];
//(2)标准方法:记录点(x,y)的前驱
            pre[next.x][next.y] = k[i];
            q.push(next);
        }
    }
}
int main() {
    for (int i = 0; i < 30; i++)  cin >> mp[i]; //读题目给的地图数据
    bfs();
    return 0;
}

双向广搜

能不能使用

能不能改善复杂度

名虽如此但仍然没有方向感

增长越快优化越明显

看看去重条件之后bfs的劣势还明不明显,不明显可继续使用。相遇和队空为终止条件

1.网格结构

2.树状结构:从下到上和从上到下进行

hdu1195

实现

#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<sstream>
#include<cstring>
#include<cstdio>
#include<string>
#include<deque>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
#define INF 0x3f3f3f3f
#define MM(x) memset(x,0,sizeof(x))
#define MMINF(x) memset(x,INF,sizeof(x))
typedef long long LL;
const double PI=acos(-1.0);
const int N=100010;
struct info
{
    char s[5];
    int step;
};
info goal,ori;
int pos[N];
int vis[N];
inline int change(char s[])
{
    int r=0;
    for (int i=0; i<4; ++i)
        r=r*10+s[i]-'0';
    return r;
}
int T_bfs()
{
    queue<info>Qf;
    queue<info>Qb;
    Qf.push(ori);
    Qb.push(goal);
    while ((!Qf.empty())||(!Qb.empty()))
    {
        if(!Qf.empty()&&(Qf.size()>Qb.size()))//这里加了个数量判断就AC了
        {
            info now1=Qf.front();
            Qf.pop();
            for (int i=0; i<4; i++)
            {
                info v=now1;
                v.s[i]--;
                if(v.s[i]<49)
                    v.s[i]='9';
                int num=change(v.s);
                if(!pos[num])
                {
                    v.step=now1.step+1;
                    pos[num]=1;
                    vis[num]=v.step;
                    Qf.push(v);
                }
                else if(pos[num]==2)
                    return vis[num]+vis[change(now1.s)];
            }
            for (int i=0; i<4; i++)
            {
                info v=now1;
                v.s[i]++;
                if(v.s[i]>57)
                    v.s[i]='1';
                int num=change(v.s);
                if(!pos[num])
                {
                    v.step=now1.step+1;
                    pos[num]=1;
                    vis[num]=v.step;
                    Qf.push(v);
                }
                else if(pos[num]==2)
                    return vis[num]+vis[change(now1.s)];
            }
            for (int i=0; i<3; i++)
            {
                info v=now1;
                swap(v.s[i],v.s[i+1]);
                int num=change(v.s);
                if(!pos[num])
                {
                    pos[num]=1;
                    v.step=now1.step+1;
                    vis[num]=v.step;
                    Qf.push(v);
                }
                else if(pos[num]==2)
                    return vis[num]+vis[change(now1.s)];
            }
        }
 
        if(!Qb.empty())
        {
            info now2=Qb.front();
            Qb.pop();
            for (int i=0; i<4; i++)
            {
                info v=now2;
                v.s[i]--;
                if(v.s[i]<49)
                    v.s[i]='9';
                int num=change(v.s);
                if(!pos[num])
                {
                    v.step=now2.step+1;
                    pos[num]=2;
                    vis[num]=v.step;
                    Qb.push(v);
                }
                else if(pos[num]==1)
                    return vis[num]+vis[change(now2.s)];
            }
            for (int i=0; i<4; i++)
            {
                info v=now2;
                v.s[i]++;
                if(v.s[i]>57)
                    v.s[i]='1';
                int num=change(v.s);
                if(!pos[num])
                {
                    v.step=now2.step+1;
                    pos[num]=2;
                    vis[num]=v.step;
                    Qb.push(v);
                }
                else if(pos[num]==1)
                    return vis[num]+vis[change(now2.s)];
            }
            for (int i=0; i<3; i++)
            {
                info v=now2;
                swap(v.s[i],v.s[i+1]);
                int num=change(v.s);
                if(!pos[num])
                {
                    v.step=now2.step+1;
                    pos[num]=2;
                    vis[num]=v.step;
                    Qb.push(v);
                }
                else if(pos[num]==1)
                    return vis[num]+vis[change(now2.s)];
            }
        }
    }
}
int main(void)//不是特别好的方法
{
    int tcase;
    scanf("%d",&tcase);
    while (tcase--)
    {
        MM(pos);
        MM(vis);
        scanf("%s %s",ori.s,goal.s);
        ori.step=0;
        goal.step=0;
        pos[change(ori.s)]=1;
        pos[change(goal.s)]=2;
        vis[change(ori.s)]=0;
        vis[change(goal.s)]=0;
        !strcmp(ori.s,goal.s)?puts("0"):printf("%d\n",T_bfs()+1);
    }
    return 0;
}
hdu1401
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <cstring>
using namespace std;
int dir[4][2] = {{0,1},{1,0},{-1,0},{0,-1}};//四个转移 
map<int , int > V,V1; //对应是否有那个状态 
struct k{
		int x,y;
		bool operator < ( const k& b) const{
			if ( x == b.x ) return y < b.y;
				return x < b.x;
		}//排序用的 
};
struct stu{
	k a[4];
	int vis;//记录第几步 
}s[2];
int BFS(stu s,int kk){
	queue <stu> q;
	stu be,nex;
	sort(s.a,s.a+4); 
	int Z  = 0;
		for (int i = 0; i < 4; i++)
		{
			Z+= s.a[i].x << (6*i);
				Z+= s.a[i].y << (6*i+3);
		}//对应每个状态 总共八个点把其从大到小排序后即可化为一个24位的二进制来表示 
	if(!kk){		
		V[Z] = 1;
	} 
	else{
		V1[Z] = 1;
		if(V[Z]) return 1;//二次进入的时候直接判断一下有木有走过即可 
	}
 	be = s; 
	q.push(be);
	while(!q.empty())
	{
		be = q.front(); q.pop();
		if( be.vis == 4 ) break;
		for (int i = 0; i < 4; i++)//哪个 
		{
			int x = be.a[i].x,y = be.a[i].y;
			for (int j = 0; j < 4; j++)//位移 
			{
				int newx = x+dir[j][0],newy = y+dir[j][1];
				for (int z = 0; z < 4; z++)
				{
					if(z != i && newx == be.a[z].x && newy == be.a[z].y)
					{
						newx += dir[j][0]; newy += dir[j][1];
					}
				}//再继续翻 
				if (newx >= 1 && newy <= 8 && newy >= 1 && newy <= 8) 
				{
					nex = be;
					nex.a[i].x = newx; nex.a[i].y = newy;
					sort(nex.a,nex.a+4); nex.vis = be.vis + 1;
					int Z  = 0;
					for (int i = 0; i < 4; i++) // 转为24位二进制 
					{
							Z+= nex.a[i].x << (6*i);
								Z+= nex.a[i].y << (6*i+3);
					}
					if(!kk)
					{
						if(! V.count(Z)){
							V[Z] = 1;
							q.push(nex);
						}
					}
					else
					{
						if(! V1.count(Z)){
							V1[Z] = 1;
							if(V[Z]) return 1; 
							q.push(nex);
						}
					}//同理 
					
				}
				
			}
		}
	}
	return 0;
}
int main()
{
	while(~scanf("%d%d",&s[0].a[0].x,&s[0].a[0].y))
	{	 
		for (int i = 0; i < 2; i++)
		{
			for (int j = 0; j < 4; j++)
			{
				if(!i && !j) continue;
				scanf("%d%d",&s[i].a[j].x,&s[i].a[j].y);
			}
		}
		V.clear();
		V1.clear();
		s[0].vis = 0; s[1].vis = 0;//初始化 
		BFS(s[0],0); 
		if(BFS(s[1],1)) printf("YES\n");
		else printf("NO\n");
	}
	return 0;
 } 
p1023
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <unordered_map>
using namespace std;
const int N = 6;
int n;
string a[N], b[N];
int extend(queue <string>&q, unordered_map <string, int>&da, unordered_map <string, int>&db, string a[], string b[]){
     string t = q.front();
  q.pop();
   for (int i = 0; i < t.size(); i ++) 
      for (int j = 0; j < n; j ++)
    if (t.substr(i, a[j].size()) == a[j]){
     string state = t.substr(0, i) +b[j] + t.substr(i + a[j].size());
     if(db.count(state))   return da[t] + 1 + db[state];
     if(da.count(state))   continue;
     da[state] = da[t] + 1;
     q.push(state);
    } 
  return 11;
}
int bfs(string A,string B){
 queue <string> qa,qb;
 unordered_map <string, int> da,db;
 qa.push(A),      da[A] = 0;
 qb.push(B),      db[B] = 0;
  while(qa.size() && qb.size()){
  int t;
  if (qa.size() < qb.size())    t = extend(qa, da, db, a, b);
  else    t = extend(qb, db, da, b, a);
  if (t <= 10)    return  t;
 }
 return 11;
}
int main(){
 string A, B;
 cin >> A >> B;
 while (cin >> a[n] >> b[n])    n ++;
 int step = bfs(A, B);
 if (step > 10)     puts("NO ANSWER!");
 else               printf("%d\n", step);
 return 0;
}

bfs与优先队列

简单理解为图论上的Dijkstra

优先队列中的元素位置是根据优先级来排的

稀疏图邻接表链式向前星:(n+m)logn

稠密图连接矩阵(边大于点):n*n不如直接暴力

最短路径dijkstra模板
#include<bits/stdc++.h>
using namespace std;
const long long INF = 0x3f3f3f3f3f3f3f3fLL;      //这样定义的好处是: INF <= INF+x
const int N = 3e5+2;
struct edge{
int from, to;   //边:起点,终点,权值。起点from并没有用到,e[i]的i就是from
long long w;    //边:权值
    edge(int a, int b,long long c){from=a; to=b; w=c;}
};
vector<edge>e[N];   		          //存储图
struct node{
    int id; long long n_dis;          //id:结点;n_dis:这个结点到起点的距离
    node(int b,long long c){id=b; n_dis=c;}
    bool operator < (const node & a) const
    { return n_dis > a.n_dis;}
};
int n,m;
int pre[N];                          //记录前驱结点
void print_path(int s, int t) {       //打印从s到t的最短路
    if(s==t){ printf("%d ", s); return; }     //打印起点
    print_path(s, pre[t]);            //先打印前一个点
    printf("%d ", t);                 //后打印当前点。最后打印的是终点t
}
long long  dis[N];                    //记录所有结点到起点的距离
bool done[N];                         //done[i]=true表示到结点i的最短路径已经找到
void dijkstra(){
    int s = 1;                        //起点s = 1
    for (int i=1;i<=n;i++) {dis[i]=INF; done[i]=false; }    //初始化
    dis[s]=0;                         //起点到自己的距离是0
    priority_queue <node> Q;          //优先队列,存结点信息
    Q.push(node(s, dis[s]));          //起点进队列
    while (!Q.empty())   {
        node u = Q.top();             //pop出距起点s距离最小的结点u
        Q.pop();
        if(done[u.id]) continue;      //丢弃已经找到最短路径的结点。即集合A中的结点            
        done[u.id]= true;
        for (int i=0; i<e[u.id].size(); i++) {  //检查结点u的所有邻居
            edge y = e[u.id][i];       //u.id的第i个邻居是y.to
            if(done[y.to]) continue;   //丢弃已经找到最短路径的邻居结点                
            if (dis[y.to] > y.w + u.n_dis) {
                dis[y.to] = y.w + u.n_dis;
                Q.push(node(y.to, dis[y.to]));    //扩展新邻居,放到优先队列中
                pre[y.to]=u.id;        //如果有需要,记录路径
            }
        }
    }
    // print_path(s,n);                //如果有需要,打印路径: 起点1,终点n
}
int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)   e[i].clear();
    while (m--) {
        int u,v,w;   scanf("%d%d%lld",&u,&v,&w);
        e[u].push_back(edge(u,v,w));
     // e[v].push_back(edge(v,u,w));     //本题是单向边
    }
    dijkstra();
    for(int i=1;i<=n;i++){
        if(dis[i]>=INF)  cout<<"-1 ";
        else   printf("%lld ", dis[i]);
    }
}

图中dijkstra的应用

E.仙人掌墙
每次测试的时间限制2秒
每次测试的内存限制256兆字节
输入标准输入
输出标准输出
Monocarp正在玩Minecraft,他想建造一堵仙人掌墙。他想把它建在一块大小为n×m单元的沙地上。最初,场地的一些单元里有仙人掌。请注意,在Minecraft中,仙人掌不能生长在侧面相邻的单元格上--而初始场地符合这一限制。Monocarp可以种植新的仙人掌(它们也必须满足前述条件)。他不能砍掉任何已经生长在田地上的仙人掌--他没有斧头,而且仙人掌对他的手来说太扎手了。

莫诺卡普认为,如果从田地的最上面一排到最下面一排没有路径,那么这堵墙就是完整的,这样。

路径上的每两个连续单元都是相邻的。
属于该路径的单元格中没有仙人掌。
你的任务是种植最小数量的仙人掌来建造一堵墙(或报告说这是不可能的)。

输入
第一行包含一个整数t--测试案例的数量。

每个测试案例的第一行包含两个整数n和m(2≤n,m≤2⋅1052≤n,m≤2⋅105;n×m≤4⋅105n×m≤4⋅105)--分别为行和列的数量。

然后n行,第i行包含一个长度为m的字符串si,其中si,j为'#',如果在第i行和第j列的交汇处长有仙人掌。否则,si,jsi,j为'.'。

所有测试案例的n×m之和不超过4⋅1054⋅105。

输出
对于每个测试案例,如果不可能在不违反规则的情况下建造仙人掌墙,则在第一行打印NO。否则,在第一行打印 "是",然后打印n行,每行有mm个字符--字段本身,其中第ii行的第j个字符等于 "#",如果在第ii行和第j列的交叉点上有一个仙人掌,否则就是"。如果有多个最佳答案,则打印其中任何一个。

import java.util.Scanner;
import java.util.PriorityQueue;

public class Main{

	static final int N = (int)4e5 + 9;
	static boolean[][] field;
	static boolean[] fixed = new boolean[N];
	static int[] pre = new int[N], head = new int[N], dist = new int[N];
	static int[][] e = new int[N << 2][3];
	static int tot, des;
	static final int inf = 0x3f3f3f3f;

	public static void main(String[] args){
		int i, j, m, n, tt;
		int u, v, w;
		String str;
		Scanner in = new Scanner(System.in);
		StringBuilder ans = new StringBuilder();
		
		tt = in.nextInt();
		while(tt -- > 0){
			n = in.nextInt();
			m = in.nextInt();
			field = new boolean[n + 9][m + 9];
			for(i = n * m; i > 0; -- i) dist[i] = inf;
			for(i = 1; i <= n; ++ i){
				str = in.next();
				for(j = 0; j < m; ++ j)
					field[i][j + 1] = str.charAt(j) == '#';
			}
			for(i = 1, tot = 0; i <= n; ++ i){
				for(j = 2; j <= m; ++ j){
					if(field[i][j - 1] || field[i][j + 1]) continue;
					if(field[i - 1][j] || field[i + 1][j]) continue;
					v = (i - 1) * m + j; w = 1;
					if(field[i][j]) w = 0;
					if(i > 1){
						u = (i - 1 - 1) * m + j - 1;
						ae(u, v, w);
						if(j < m){
							u = (i - 1 - 1) * m + j + 1;
							ae(u, v, w);
						}
					}
					if(i < n){
						u = (i + 1 - 1) * m + j - 1;
						ae(u, v, w);
						if(j < m){
							u = (i + 1 - 1) * m + j + 1;
							ae(u, v, w);
						}
					}
				}
			}
			for(i = v = 1, u = 0; i <= n; ++ i, v += m){
				if(field[i][2]) continue;
				if(field[i - 1][1]|| field[i + 1][1]) continue;
				if(field[i][1]) w = 0;
				else w = 1;
				ae(u, v, w);
			}
			dijkstra(0, m);
			if(des > 0){
				ans.append("YES\n");
				while(des > 0){
					i = (des + m - 1) / m; j = des % m;
					if(j == 0) j = m;
					field[i][j] = true; des = pre[des];
				}
				for(i = 1; i <= n; ++ i){
					for(j = 1; j <= m; ++ j)
						if(field[i][j]) ans.append('#');
						else ans.append('.');
					ans.append('\n');
				}
			}
			else ans.append("NO\n");
			
			for(i = n * m; i >= 0; -- i){
				head[i] = 0; fixed[i] = false;
			}
		}
		System.out.printf("%s", ans);
	}
	
	static void ae(int u, int v, int w){
		e[++ tot][0] = v; e[tot][1] = head[u];
		e[tot][2] = w; head[u] = tot;
	}
	
	static void dijkstra(int s, int m){
		int i, u, v, w;
		PriorityQueue<node> pq = new PriorityQueue<>();
		pq.add(new node(s, 0));
		while(!pq.isEmpty()){
			u = pq.poll().ind;
			if(u > 0 && u % m == 0){
				des = u; break;
			}
			for(i = head[u]; i > 0; i = e[i][1]){
				v = e[i][0]; w = e[i][2];
				if(dist[v] > dist[u] + w){
					dist[v] = dist[u] + w; pre[v] = u;
					pq.add(new node(v, dist[v]));
				}
			}
		}
	}
}

class node implements Comparable<node>{
	int ind, dis;
	node(int a, int b){
		ind = a; dis = b;
	}	
    //public int compare(node o1, node o2) {
    //        return o2.dis - o1.dis; //大的在前
    //   }
	public int compareTo(node z){
		if(dis < z.dis) return -1;
		else if(dis > z.dis) return 1;
		return 0;
	}
}

bfs与双端队列

解决图的边权为1or0的特殊图情况

switch the lamp on
#include<bits/stdc++.h>
using namespace std;
const int dir[4][2] = {{-1,-1},{-1,1},{1,-1},{1,1}}; //4个方向的位移
const int ab[4] = {2,1,1,2};                         //4个元件期望的方向
const int cd[4][2] = {{-1,-1},{-1,0},{0,-1},{0,0}};  //4个元件编号的位移
int graph[505][505],dis[505][505];                   //dis记录结点到起点s的最短路
struct P{ int x,y,dis; }u;
int read_ch(){
    char c;
    while((c = getchar())!='/' && c != '\\') ;   //字符不是'/'和'\'
    return c=='/' ? 1 : 2;
}
int main(){
    int n, m; cin >>n >>m;
    memset(dis,0x3f,sizeof(dis));
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)   graph[i][j] = read_ch();
    deque <P> dq;
    dq.push_back((P){1,1,0}); 
    dis[1][1] = 0;
    while(!dq.empty()){
        u = dq.front(), dq.pop_front();   //front()读队头,pop_front()弹出队头
        int nx,ny;
        for(int i=0;i<=3;++i) {           //4个方向
            nx = u.x+dir[i][0];  ny = u.y+dir[i][1];
            int d = 0;                    //边权
            d = graph[u.x+cd[i][0]][u.y+cd[i][1]]!=ab[i];   //若方向不相等,则d=1
            if(nx && ny && nx<n+2 && ny<m+2 && dis[nx][ny]>dis[u.x][u.y]+d){
              //    如果一个结点再次进队,那么距离应该更小。实际上,
                //由于再次进队时,距离肯定更大,所以这里的作用是阻止再次入队
                  dis[nx][ny] = dis[u.x][u.y]+d; 
                  if(d==0)  dq.push_front((P){nx, ny, dis[nx][ny]});  //边权=0,插到队头                                          
                  else dq.push_back ((P){nx, ny, dis[nx][ny]});       //边权=1,插到队尾
                  if(nx==n+1 && ny==m+1) break;     //到终点退出。不退也行,队列空自动退                    
            }
        }
    }
    if(dis[n+1][m+1] != 0x3f3f3f3f)  cout << dis[n+1][m+1];
    else   cout <<"NO SOLUTION";         //可能无解,即s到t不通
    return 0;
}

A*

贪心最优和dijkstra

贪心出该点距离到终点的最短距离
dijkstra求出到该点最短的路径
f=g+h
对i的评估=起点到该点的代价+该点到终点的代价
g h应当是同样的计算方法 h应该优于所有实际存在的路径

第k路径最短
// poj 2449代码
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 1005, M = 100005;
struct edge{          //记录边
int to, w;
//vector edge[i]:起点是i;它有很多边,其中一个边的to是边的终点,w是边长
    edge(int a,int b){ to = a, w = b;} //赋值
};
vector <edge>G[M], G2[M];  //G:原图; G2:反图
struct node {      //用于dijkstra。记录点,以及点到起点的路径
    int id, dis;   //id:点;dis:点id到起点的路径长度
    node(int a, int b){ id = a, dis = b;} //赋值
    bool operator < (const node &u) const { return dis > u.dis; }
};
int  dist[N];   //dist[i]: 从s到点i的最短路长度
bool done[N];   //done[i]=ture: 表示到i的最短路已经找到
void dijkstra(int s) {    //标准的dijkstra: 求s到其他所有点的最短路
    for(int i =0;i<N;i++) {dist[i]=INF; done[i]=false;}  //初始化
    dist[s] = 0;          //起点s到自己的距离是0
    priority_queue<node> q;
    q.push(node(s, dist[s]));    //从起点开始处理队列
    while (!q.empty()) {
        node u = q.top();        //pop出距起点s最近的点u
        q.pop();
        if (done[u.id])  continue; //丢弃已经找到最短路的点            
        done[u.id] = true;       //标记:点u到s的最短路已经找到
        for (int i = 0; i< G2[u.id].size(); i++) {  //检查点u的所有邻居
            edge y = G2[u.id][i];
            if (done[y.to])   continue; //丢弃已经找到最短路的邻居                
            if (dist[y.to] > u.dis + y.w) {
                dist[y.to] = u.dis + y.w;
                q.push(node(y.to, dist[y.to]));  //扩展新的邻居,放进优先队列
            }
        }
    }
}
struct point {      //用于 astar
    int v, g, h;    //评估函数 f = g + h, g是从s到i的长度,h是从i到t的长度
    point(int a, int b, int c) { v=a, g=b, h=c; }
    bool operator < (const point & b) const { return g + h > b.g + b.h;}
};
int times[N];     //times[i]: 点i被访问的次数
int astar(int s, int t, int k){
    memset(times, 0, sizeof(times));
    priority_queue<point> q;
    q.push(point(s, 0, 0));
    while (!q.empty()) {
        point p = q.top();   //从优先队列中弹出f = g + h最小的
        q.pop();
        times[p.v]++;
        if (times[p.v] == k && p.v == t)  //从队列中第k次弹出t,就是答案
            return p.g + p.h;
        for (int i = 0; i< G[p.v].size(); i++) {
            edge y = G[p.v][i];
            q.push(point(y.to, p.g + y.w, dist[y.to]));
        }
    }
    return -1;
}
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    while (m--) {
        int a, b, w;             //读边:起点、终点、边长
        scanf("%d%d%d", &a, &b, &w);  //本题是有向图
         G[a].push_back(edge(b,w));  //原图
        G2[b].push_back(edge(a,w));  //反图
    }
    int s, t, k;
    scanf("%d%d%d", &s, &t, &k);
    if (s == t)  k++;         //一个小陷阱
    dijkstra(t);              //在反图G2上,求终点t到其他点的最短路
    printf("%d\n", astar(s, t, k));  //在原图G上,求第k短路
    return 0;
}

IDA* IDDFS

应用

有效基因变换(bfs 双向bfs A* 图论)

基因序列可以表示为一条由 8 个字符组成的字符串,其中每个字符都是 'A''C''G''T' 之一。

假设我们需要调查从基因序列 start 变为 end 所发生的基因变化。一次基因变化就意味着这个基因序列中的一个字符发生了变化。

  • 例如,"AACCGGTT" --> "AACCGGTA" 就是一次基因变化。

另有一个基因库 bank 记录了所有有效的基因变化,只有基因库中的基因才是有效的基因序列。(变化后的基因必须位于基因库 bank 中)

给你两个基因序列 startend ,以及一个基因库 bank ,请你找出并返回能够使 start 变化为 end 所需的最少变化次数。如果无法完成此基因变化,返回 -1

注意:起始基因序列 start 默认是有效的,但是它并不一定会出现在基因库中。

示例 1:

输入:start = "AACCGGTT", end = "AACCGGTA", bank = ["AACCGGTA"]
输出:1

BFS

为了方便,我们令 S=start、 T=end,将每个基因序列视为「状态」。

容易想到使用 BFS 进行求解,并使用「哈希表」记录到达某个状态所消耗的步数(同时为了快速判断某个状态是否合法,我们使用 Set 结构对 bank[i] 进行转存)。

起始将 S 加入队列,并更新到达 S 所使用的步数为 0,然后进行常规的 BFS 过程:每次取出队头元素,尝试替换当前状态的某一位,来得到新的状态(限定新状态必须合法,即必须出现在 Set 中),如果新状态合法并且没有在记录步数的哈希表中出现过,则将新状态入队并更新得到新状态所用步数,否则丢弃新状态。

重复上述过程直到找到 T(返回具体步数) 或者队列为空(返回 −1)。

代码:

Java

class Solution {
    static Deque<String>d;
    static char chs[]=new char[]{'A','C','G','T'};//根据题目规定  你ABCD是什么牛马
    static String S,T;
    static Map<String,Integer>map=new HashMap<>();//切记切记因为有很多样例所以必须clear
    static Set<String>banks=new HashSet<>();//同上
    int res=0;//完全不要static
    public int minMutation(String start, String end, String[] bank) {
        d=new ArrayDeque<>();
        S=start;T=end;
        for(String s:bank){
            banks.add(s);
        }
        d.addLast(S);
        map.put(S,0);
        bfs();
        map.clear();//clear
        banks.clear();//clear
        d.clear();//
        return res;
    }
    void bfs(){
         while(!d.isEmpty()){
        //      int size = d.size();// 广度优先搜索, 把这一层所有的元素都取出来进行操作
        //     while (size-- > 0) {//没有也可以
            String tmp=d.pollFirst();
            char[] mchs=tmp.toCharArray();
            int step=map.get(tmp);
            for(int i=0;i<8;i++){
                for(char c:chs){
                    if(mchs[i]==c)continue;
                    char[]clone=mchs.clone();
                    clone[i]=c;
                    String sub=String.valueOf(clone);
                    if(!banks.contains(sub))continue;
                    if(map.containsKey(sub))continue;
                    if(sub.equals(T)){res=step+1;return;}
                    map.put(sub,step+1);
                    d.addLast(sub);
                }

            }
     //   }
        }
        res=-1;
        return;
    }
}
  • 时间复杂度:令 n为 bank 的数组长度(合法状态数),将 bank 存入 Set 结构复杂度为 O(n),每个状态经过一步操作最多拓展出 C=32 个新基因(共有 8 个位置,每个位置有 4个选择),BFS 过程复杂度为 O(C∗n)。整体复杂度为 O(C∗n)
  • 空间复杂度:O(n)

双向 BFS

同理,我们可以使用「双向 BFS」进行求解。

双向 BFS 与常规 BFS 相比,能够有效解决「搜索空间爆炸」的问题:

img

对双向 BFS 不熟悉的同学可以看前置

标签:return,int,father,基础,算法,搜索,new,include,dis
From: https://www.cnblogs.com/yyzAC/p/16940129.html

相关文章

  • 最小生成树算法及其常见应用
    最小生成树定义:在无向图\(G=(V,E)\)中,一颗连接所有节点的树(无根树)被称为这个无向图的生成树,其中边权之和最小的那一颗被称为最小生成树定理:最小生成树一定包含无向图中......
  • 最短路算法及其常见扩展应用
    第一节——最短路问题基本概念:由于无向边可以看作两条相反的有向边,于是我们默然按照有向边的形式讨论存图方式:邻接矩阵:空间复杂度\(O(n^2)\),优点:\(O(1)\)查找\(x->y\)......
  • HTTP与HTML基础知识
    今日内容概要前端与后端的概念超文本传输协议HTTP超文本标记语言HTML今日内容详细前端与后端的概念前端用于展示及获取数据的界面就是前端,前端的终点在于页面设计......
  • HTML基础标签
    目录前端的三剑客web的服务本质HTTP协议HTML语法概括head内的常见标签boby内基本标签常见符号body内布局标签baby内常用标签列表标签表格标签表单标签input前端的三剑客......
  • Java中进制基础知识与算法题
    本篇文章旨在给大家普及下计算机内部数据的机器级表示方式,即:二进制、八进制、十进制、十六进制…对于进制,我们从小最先接触的是十进制,这个也是我们日常生活中应用最多的数......
  • day45前端开发基础(1)
    目录前端与后端的概念前端三剑客前端前戏HTTP协议HTML简介HTML概览预备知识head内常见标签body内基本标签常见符号body内布局标签body内常用标签列表标签表格标签表单标签......
  • 代码随想录算法训练营Day14|144. 二叉树的前序遍历、94. 二叉树的中序遍历、145. 二叉
    代码随想录算法训练营Day14|144.二叉树的前序遍历、94.二叉树的中序遍历、145.二叉树的后序遍历144.二叉树的前序遍历144.二叉树的前序遍历递归遍历/***Defini......
  • 力扣 leetcode 33. 搜索旋转排序数组
    问题描述整数数组nums按升序排列,数组中的值互不相同。在传递给函数之前,nums在预先未知的某个下标k(0<=k<nums.length)上进行了旋转,使数组变为[nums[k],nums[k......
  • 前端基础——前后端的概念、服务端搭建及客户端访问、超文本传输协议(HTTP)、超文本标
    前端基础——前后端的概念、服务端搭建及客户端访问、超文本传输协议(HTTP)、超文本标记语言(HTML)一、前端和后端的概念前端 任何与用户直接打交道的操作界面都可以称......
  • 菜鸟好文推荐(十五)——9个基于Java的搜索引擎框架
    在这个信息相当繁杂的互联网时代,我们已经学会了如何利用搜索引擎这个强大的利器来找寻目标信息,比如你会在Google上搜索情人节如何讨女朋友欢心,你也会在百度上寻找正规的整......