BFS中的双向广搜和A-star

双向广搜

190. 字串变换 - AcWing题库

import java.util.*;

public class Main{
    static int N = 6, n = 0;//规则数不超过6个,n表示规则数量
    static String[] a = new String[N];//字串a,用于规则变换
    static String[] b = new String[N];//字串b,用于规则变换
    
    public static int expend(Queue<String> q, Map<String, Integer> da, Map<String, Integer> db, String[] a, String[] b){
        int d = da.get(q.peek());//看一下队头元素到相应起点的距离
        
        //遍历这个队列所有距离一样的这一层的所有状态
        while(!q.isEmpty() && da.get(q.peek()) == d){
            String t = q.poll();//弹出队头
            
            for(int i = 0; i < t.length(); i ++){//从取出的这个队头元素的第一个字母开始遍历
                for(int j = 0; j < n; j ++){//遍历n个规则
                    if(i + a[j].length() > t.length()) continue;//如果超出字符串长度就continue
                    
                    if(t.substring(i, i + a[j].length()).equals(a[j])){
                        //三段式更新
                        String state = t.substring(0, i) + b[j] + t.substring(i + a[j].length());
                        //如果b中有,就说明找到了
                        if(db.containsKey(state)) return da.get(t) + db.get(state) + 1;
                        //如果a中有就说明已经遍历过l,直接continue
                        if(da.containsKey(state)) continue;
                        
                        da.put(state, da.get(t) + 1);//把这个能到达的状态加进去
                        q.add(state);//加进队列
                    }
                }
            }
        }
        return 11;
    }
    
    public static int bfs(String A, String B){
        if(A.equals(B)) return 0;//如果一开始就相等就直接返回0
        
        Queue<String> qa = new LinkedList<>();//队列a 从上面出发
        Queue<String> qb = new LinkedList<>();//队列b 从下面出发
        Map<String, Integer> da = new HashMap<>();//到上面起点的距离
        Map<String, Integer> db = new HashMap<>();//到下面起点的距离
        
        //初始化
        qa.add(A); da.put(A, 0);
        qb.add(B); db.put(B, 0);
        
        while(!qa.isEmpty() && !qb.isEmpty()){
            int t = 0;
            if(qa.size() <= qb.size()) t = expend(qa, da, db, a, b);//a,b表示规则
            else t = expend(qb, db, da, b, a);//如果从下往上,参数需要反过来写
            
            if(t <= 10) return t;//有解,返回
        }
        return 11;
    }
    
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        String A = sc.next();
        String B = sc.next();
        
        while(sc.hasNext()){
            a[n] = sc.next();
            b[n] = sc.next();
            n ++;
        }
        
        int res = bfs(A, B);
        if(res > 10) System.out.println("NO ANSWER!");
        else System.out.print(res);
    }
}

A star问题

1.A*算法一定要在保证有解的条件下用。

2.另外还要满足估计值一定要小于等于真实值。

3.A*算法除了终点之外,并不能保证其他点第一次出队时是最优解,也不能保证每个点只被扩展一次。

179. 八数码 - AcWing题库

如果给出的字母序列中逆序对的数量是偶数,那么就有解,如果是奇数,就没有解。(这对于所有奇数码都是成立的,即n为奇数,例如十五数码)
这道题的估计函数就是:当前状态到它的目标状态的曼哈顿距离之和

Java:StringBuilder的基本使用_java stringbuilder用法-CSDN博客

java中charAt()方法和setcharAt()方法-CSDN博客

比较的模板

import java.util.*;

class PII{
    String prestate;//前一个状态
    char op;//操作
    
    public PII(String prestate, char op){
        this.prestate = prestate;
        this.op = op;
    }
}

class Item implements Comparable<Item>{
    String state;//状态
    int sum_dist;//估计距离
    
    public Item(String state, int sum_dist){
        this.state = state;
        this.sum_dist = sum_dist;
    }
    
    public int compareTo(Item o){//小根堆
        return sum_dist - o.sum_dist;
    }
}

public class Main{
    //用来算估计距离
    public static int f(String start){//即每个数的当前状态到目标状态的曼哈顿距离
        int d = 0;//距离之和
        for(int i = 0; i < 9; i ++){
            if(start.charAt(i) == 'x') continue;
            int t = start.charAt(i) - '1';
            d = d + Math.abs(t / 3 - i / 3) + Math.abs(t % 3 - i % 3);
        }
        return d;
    }
    
    //交换位置
    public static String swap(String t, int x, int y){
        StringBuilder sb = new StringBuilder(t);
        sb.setCharAt(x, t.charAt(y));
        sb.setCharAt(y, t.charAt(x));
        return sb.toString();
    }
    
    public static String bfs(String start){
        PriorityQueue<Item> pq = new PriorityQueue<>();//优先队列
        Map<String, PII> pre = new HashMap<>();//用来记录某个状态是从哪个状态经过哪个操作得来的
        Map<String, Integer> dist = new HashMap<>();//用来存距离
        
        pq.add(new Item(start, 0 + f(start)));
        dist.put(start, 0);
        
        int[] dx = {-1, 0, 1, 0}, dy = {0, 1, 0, -1};//四个方向
        char[] ope = {'u', 'r', 'd', 'l'};//四个操作
        String end = "12345678x";//目标字符串
        
        while(!pq.isEmpty()){
            String t = pq.poll().state;//队头元素的状态
            
            if(t.equals(end)) break;//如果已经相等
            
            //寻找t中字符x的位置
            int x = -1, y = -1;
            for(int i = 0; i < 9; i ++){
                if(t.charAt(i) == 'x'){
                    x = i / 3;
                    y = i % 3;
                    break;
                }
            }
            
            //四个操作方向
            for(int i = 0; i < 4; i ++){
                int a = x + dx[i];
                int b = y + dy[i];
                if(a < 0 || b < 0 || a >= 3 || b >= 3) continue;//越界
                
                String state = swap(t, x * 3 + y, a * 3 + b);
                //没有遍历过或者距离大于当前加1
                if(!dist.containsKey(state) || dist.get(state) > dist.get(t) + 1){
                    dist.put(state, dist.get(t) + 1);
                    pre.put(state, new PII(t, ope[i]));
                    pq.add(new Item(state, dist.get(state) + f(state)));
                }
            }
        }
        StringBuilder sb = new StringBuilder();
        while(end != start){
            sb.append(pre.get(end).op);
            end = pre.get(end).prestate;
        }
        return sb.reverse().toString();//记得要翻转输出
    }
    
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        String start = "";//最初的字串
        String seq = "";//用来求逆序对的数量
        //输入数据
        for(int i = 0; i < 9; i ++){
            String c = sc.next();
            start += c;
            if(!c.equals("x")) seq += c;
        }
        //开始求逆序对的数量
        int cnt = 0;//用cnt表示逆序对的数量
        for(int i = 0; i < 8; i ++){
            for(int j = i; j < 8; j ++){//j要从i开始
                if(seq.charAt(i) > seq.charAt(j)) cnt ++;
            }
        }
        //如果逆序对的数量cnt为奇数,则不存在解决方案
        if(cnt % 2 == 1) System.out.print("unsolvable");
        else System.out.print(bfs(start));
    }
}

178. 第K短路 - AcWing题库

这里的估价函数是每个点到终点的最短距离,那么也就是说可用Dijsktra算法反向求一下终点到各个点的最短距离。
这个代码超时了,但是还没找到问题在哪里,下次再看看吧

import java.util.*;
import java.io.*;

class PIIs implements Comparable<PIIs>{
    int num;//当前点
    int sum_distance;//估计距离+真实距离
    int distance;//真是距离
    public PIIs(int num, int sum_distance, int distance){
        this.num = num;
        this.sum_distance = sum_distance;
        this.distance = distance;
    }
    public int compareTo(PIIs o){
        return this.distance - o.distance;
    }
}

//Dijsktra算法堆优化版
class PII implements Comparable<PII>{
    int distance;//距离
    int num;//几号点
    public PII(int distance, int num){
        this.distance = distance;
        this.num = num;
    }
    public int compareTo(PII o){
        return this.distance - o.distance;//小根堆
    }
}

public class Main{
    static int N = 1010, M = 20010;
    //其中rh是反向邻接表表头
    static int[] h = new int[N], rh = new int[N], e = new int[M], ne = new int[M], w = new int[M];
    static int idx, S, T, K, n;
    static int[] dist = new int[N];//当前点到终点的最短距离
    static boolean[] st = new boolean[N];//用于dijkstra算法
    
    public static void add(int[] h, int a, int b, int c){
        e[idx] = b;
        w[idx] = c;
        ne[idx] = h[a];
        h[a] = idx ++;
    }
    
    //dijkstra算法模板
    public static void dijkstra(){
        PriorityQueue<PII> q = new PriorityQueue<>();
        Arrays.fill(dist, 0x3f3f3f3f);
        dist[T] = 0;//初始化终点到终点的距离
        q.add(new PII(0, T));//加入优先队列
        
        while(!q.isEmpty()){
            PII p = q.poll();//取出队头
            
            int t = p.num;
            
            if(st[t]) continue;//遍历过了
            st[t] = true;
            
            for(int i = rh[t]; i != -1; i = ne[i]){
                int j = e[i];
                if(dist[j] > dist[t] + w[i]){
                    dist[j] = dist[t] + w[i];
                    q.add(new PII(dist[j], j));
                }
            }
        }
    }
    
    public static int astar(){
        PriorityQueue<PIIs> q = new PriorityQueue<>();
        // 如果起点和终点不连通,直接返回-1
        if(dist[S] == 0x3f3f3f3f) return -1;
        q.add(new PIIs(S, dist[S], 0));
        
        int cnt = 0;//记录被弹出的次数
        while(!q.isEmpty()){
            PIIs p = q.poll();
            int t = p.num;
            int distance = p.distance;
            if(t == T) cnt ++;//等于终点
            if(cnt == K) return distance;
            
            for(int i = h[t]; i != -1; i = ne[i]){
                int j = e[i];
                q.add(new PIIs(j, distance + w[i] + dist[j], distance + w[i]));
            }
        }
        return -1;
    }
    
    public static void main(String[] args)throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        Arrays.fill(h, -1);//初始化邻接表表头
        Arrays.fill(rh, -1);
        
        String[] s = br.readLine().split(" ");
        n = Integer.parseInt(s[0]);
        int m = Integer.parseInt(s[1]);
        
        for(int i = 0; i < m; i ++){
            String[] s1 = br.readLine().split(" ");
            int a = Integer.parseInt(s1[0]);
            int b = Integer.parseInt(s1[1]);
            int c = Integer.parseInt(s1[2]);
            add(h, a, b, c);
            add(rh, b, a, c);
        }
        
        String[] s2 = br.readLine().split(" ");
        S = Integer.parseInt(s2[0]);
        T = Integer.parseInt(s2[1]);
        K = Integer.parseInt(s2[2]);
        
        dijkstra();
        if(S == T) K ++;//每条最短路中至少要有一条边
        
        System.out.print(astar());
    }
}
相关推荐
ChoSeitaku23 分钟前
链表循环及差集相关算法题|判断循环双链表是否对称|两循环单链表合并成循环链表|使双向循环链表有序|单循环链表改双向循环链表|两链表的差集(C)
c语言·算法·链表
Fuxiao___32 分钟前
不使用递归的决策树生成算法
算法
我爱工作&工作love我37 分钟前
1435:【例题3】曲线 一本通 代替三分
c++·算法
白-胖-子1 小时前
【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-统计数字
开发语言·c++·算法·蓝桥杯·等考·13级
workflower1 小时前
数据结构练习题和答案
数据结构·算法·链表·线性回归
好睡凯1 小时前
c++写一个死锁并且自己解锁
开发语言·c++·算法
Sunyanhui11 小时前
力扣 二叉树的直径-543
算法·leetcode·职场和发展
一个不喜欢and不会代码的码农2 小时前
力扣105:从先序和中序序列构造二叉树
数据结构·算法·leetcode
前端郭德纲2 小时前
浏览器是加载ES6模块的?
javascript·算法
SoraLuna2 小时前
「Mac玩转仓颉内测版10」PTA刷题篇1 - L1-001 Hello World
算法·macos·cangjie