蓝桥真题练习——路径

小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图中的最短路径。
  小蓝的图由 2021 个结点组成,依次编号 1 至 2021。对于两个不同的结点 a,b,如果 a 和 b 的差的绝对值大于 21,则两个结点之间没有边相连;如果a 和 b 的差的绝对值小于等于21,则两个点之间有一条长度为 a 和 b 的最小公倍数的无向边相连。
  例如:结点1 和结点 23 之间没有边相连;结点 3 和结点 24 之间有一条无向边,长度为 24;结点15 和结点 25 之间有一条无向边,长度为 75。
  请计算,结点1 和结点1 2021 之间的最短路径长度是多少。
  提示:建议使用计算机编程解决问题。

建一个有2021 个顶点 21 × 2000 +[21 ( 21 + 1 ) ]/2 条边的无向图
记忆化搜索

将已经搜索到的节点到目标节点间的最短路径保存下来,在再次搜索到这个 “后缀” 的分支时直接返回

import java.util.ArrayList;
import java.util.List;

public class Test {

    public static void main(String[] args) { new Test().run(); }

    int N = 2021;

    int[] weight = new int[N + 1];

    List<Edge>[] graph = new List[N + 1];

    boolean[] visited = new boolean[N + 1];

    void run() {
        for (int i = 1; i <= N; i++)
            graph[i] = new ArrayList();
        for (int v = 1; v <  N; v++)
            for (int w = v + 1; w <= min(v + 21, N);  w++) {
                graph[v].add(new Edge(w, lcm(v, w)));
                graph[w].add(new Edge(v, lcm(v, w)));
            }
        visited[1] = true;
        System.out.println(dfs(1));
    }

    int dfs(int v) {
        if (v == N) return 0;
        if (weight[v] != 0) return weight[v];
        int min = 0x7FFFFFFF;
        for (Edge edge : graph[v]) {
            if (visited[edge.w]) continue;
            visited[edge.w] = true;
            min = min(min, dfs(edge.w) + edge.weight);
            visited[edge.w] = false;
        }
        return weight[v] = min;
    }

    int min(int a, int b) { return a < b ? a : b; }

    int lcm(int a, int b) { return a * b / gcd(a, b); }

    int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }

    class Edge {

        int w, weight;

        Edge(int w, int weight) {
            this.weight = weight;
            this.w = w;
        }
    }
}

枝减
import java.util.PriorityQueue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Queue;
import java.util.List;

public class Test {

    public static void main(String[] args) { new Test().run(); }

    int N = 2021;
    
    void run() {
        List<Edge>[] graph = new List[N + 1];
        long[] visited = new long[N + 1];
        for (int i = 1; i <= N; i++)
            graph[i] = new ArrayList();
        for (int v = 1; v <  N; v++)
            for (int w = v + 1; w <= min(v + 21, N);  w++) {
                graph[v].add(new Edge(w, lcm(v, w)));
                graph[w].add(new Edge(v, lcm(v, w)));
            }
        Queue<Vertex> queue = new PriorityQueue();
        Arrays.fill(visited, Long.MAX_VALUE);
        queue.offer(new Vertex(1, 0));
        Vertex V = null;
        while (queue.size() > 0) {
            V = queue.poll();
            if (V.v == N) break;
            if (V.weight >= visited[V.v]) continue;
            visited[V.v] = V.weight;
            for (Edge edge : graph[V.v])
                queue.offer(new Vertex(edge.w, edge.weight + V.weight));
        }
        System.out.println(V.weight);
    }

    int min(int a, int b) { return a < b ? a : b; }

    int lcm(int a, int b) { return a * b / gcd(a, b); }

    int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }

    class Edge {

        int w, weight;

        Edge(int w, int weight) {
            this.weight = weight;
            this.w = w;
        }
    }

    class Vertex implements Comparable<Vertex> {

        int v;
        long weight;

        Vertex(int v, long weight) {
            this.weight = weight;
            this.v = v;
        }

        @Override
        public int compareTo(Vertex V) { return Long.compare(this.weight, V.weight); }
    }
}

双向搜索

越是编号大的节点,连接着它的边的权重可能越大。也就是在最短路径的这条分支中,越是靠近目标节点,就越可能进入无效的分支。

从源点和终点双向开始搜索,当两条分支相遇时,即视为找到了最短路径

import java.util.PriorityQueue;
import java.util.ArrayList;
import java.util.Queue;
import java.util.List;

public class Test {

    public static void main(String[] args) { new Test().run(); }

    int N = 2021;

    void run() {
        List<Edge>[] graph = new List[N + 1];
        long[] visited0 = new long[N + 1];
        long[] visited1 = new long[N + 1];
        for (int i = 1; i <= N; i++) {
            graph[i] = new ArrayList();
            visited0[i] = visited1[i] = Long.MAX_VALUE;
        }
        for (int v = 1; v <  N; v++)
            for (int w = v + 1; w <= min(v + 21, N);  w++) {
                graph[v].add(new Edge(w, lcm(v, w)));
                graph[w].add(new Edge(v, lcm(v, w)));
            }
        Queue<Vertex> queue = new PriorityQueue();
        queue.offer(new Vertex(N, 0, false));
        queue.offer(new Vertex(1, 0));
        Vertex V = null;
        while (true) {
            V = queue.poll();
            if (V.fromHead) {
                if (visited1[V.v] != Long.MAX_VALUE) break;
                if (V.weight >= visited0[V.v]) continue;
                visited0[V.v] = V.weight;
            } else {
                if (visited0[V.v] != Long.MAX_VALUE) break;
                if (V.weight >= visited1[V.v]) continue;
                visited1[V.v] = V.weight;
            }
            for (Edge edge : graph[V.v])
                queue.add(new Vertex(edge.w, edge.weight + V.weight, V.fromHead));
        }
        System.out.println(V.weight + (V.fromHead ? visited1[V.v] : visited0[V.v]));
    }

    int min(int a, int b) { return a < b ? a : b; }

    int lcm(int a, int b) { return a * b / gcd(a, b); }

    int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }

    class Edge {

        int w, weight;

        Edge(int w, int weight) {
            this.weight = weight;
            this.w = w;
        }
    }

    class Vertex implements Comparable<Vertex> {

        int v;
        long weight;
        boolean fromHead;

        Vertex(int v, long weight) { this(v, weight, true); }

        Vertex(int v, long weight, boolean fromHead) {
            this.fromHead = fromHead;
            this.weight = weight;
            this.v = v;
        }

        @Override
        public int compareTo(Vertex V) { return Long.compare(this.weight, V.weight); }
    }
}

动态规划
public class Test {

    public static void main(String[] args) { new Test().run(); }

    int N = 2021;

    void run() {
        long[] dp = new long[N + 1];
        for (int w = 2; w <= N; w++) {
            dp[w] = Long.MAX_VALUE;
            for (int v = w - 1; v > 0 && v >= w - 21; v--)
                dp[w] = min(dp[w], dp[v] + lcm(v, w));
        }
        System.out.println(dp[N]);
    }

    long min(long a, long b) { return a < b ? a : b; }

    int lcm(int a, int b) { return a * b / gcd(a, b); }

    int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值