双向广搜
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());
}
}