自用算法模板(JAVA版)

2019-05-21  本文已影响0人  cJaven

一、数论

1)GCD

GCD(求最大公约数)

public static int gcd(int a, int b) {
    if(b == 0) return a;
    return gcd(b, a%b);
}

QGCD(快速GCD)

public static int qGCD(int a, int b) {
    if(a == 0) return b;
    if(b == 0) return a;
    if((a & 1) == 0 && (b & 1) == 0) {
        return qGCD(a >> 1, b >> 1) << 1;
    } else if((b & 1) == 0) {
        return qGCD(a, b >> 1);
    } else if((a & 1) == 0) {
        return qGCD(a >> 1, b);
    } else {
        return qGCD(Math.abs(a - b), Math.min(a, b));
    }
}

extGCD(拓展GCD,解决ax + by = c 问题)

import java.util.Scanner;

public class 数论_拓展GCD {
    /**
     *  题目描述:
     *  两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,
     *  也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。
     *  我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度1米,这样我们就得到了一条首尾相接的数轴。设青蛙A的出发点坐标是x,青蛙B的出发点坐标是y。青蛙A一次能跳m米,青蛙B一次能跳n米,两只青蛙跳一次所花费的时间相同。
     *  纬度线总长L米。现在要你求出它们跳了几次以后才会碰面。
    * 
     *  输入只包括一行5个整数x,y,m,n,L,其中x≠y < 2000000000,0 < m、n < 2000000000,0 < L < 2100000000。
     * 
     *  输出碰面所需要的跳跃次数,如果永远不可能碰面则输出一行"Impossible" 
     * Sample Input: 
     * 1 2 3 4 5 
     * Sample Output:
     * 4
     * 
     */
    public static long extGCD(long a, long b, long x, long y) {
        if(b == 0) {
            x = 1;
            y = 0;
            return a;
        }else {
            long d = extGCD(b, a % b, y, x);
            long temp = y;
            y = x - (a / b) * y;
            x = temp;
            return d;
        }
    }

    //分析:
    //设时间为t,青蛙A在t时间后的位置为x + m * t,青蛙B在t时间后的位置y + n * t
    //因为维度线可视作为一个圈,则可推出他们相遇时候的公式:(x + (m * t)) % L = (y + (n * t)) % L
    //上式转换为:(m - n) * t + k * L = y - x;(t、k为未知数,t为次数,k为圈数)
    //满足ax + by = c
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        long x = in.nextLong();
        long y = in.nextLong();
        long m = in.nextLong();
        long n = in.nextLong();
        long L = in.nextLong();
        long a = m - n;
        long b = L;
        long c = y - x;
        long d = extGCD(a, b, x, y);
        if(c % d != 0) {
            System.out.println("Impossible");
        }else {
            x = x * c / d;
            long t = Math.abs(b / d);
            x = (x % t + t) % t;
            System.out.println(x);
        }
    }
}

2)素数(也称质数,质数定义为在大于1的自然数中,除了1和它本身以外不再有其他因数)

素数筛选

static boolean[] is_prime = new boolean[100000];

public static void prime(int maxN) {
    Arrays.fill(is_prime, true);
    for(int i = 2; i * i < maxN; i++) {
        if(is_prime[i]) {
            for(int j = i * i; j < maxN; j += i) {
                is_prime[j] = false;
            }
        }
    }
}

3)欧拉函数(小于n且和n互质(两个数的最大公约数为1)的正整数(包括1)的个数,如12的欧拉函数是φ(12)=4,有1,5,7,11)

单个数的欧拉函数

public static int euler(int n) {
    int res = n;
    for(int i = 2; i * i <= n; i++) {
        if(n % i == 0) {
            res = res - res / i;
            while(n % i == 0) {
                n /= i;
            }
        }
    }
    if(n > 1) {
        res = res - res / n;
    }
    return res;
}

欧拉函数筛选

static final int MAX = 100000;
static int[] a = new int[MAX];

public static void euler_meter(){
    for(int i = 1; i < MAX; i++){
        a[i] = i;
    }
    for(int i = 2; i < MAX; i++){
        if(a[i] == i){
            for(int j = i; j < MAX; j += i){
                a[j] = a[j] - a[j] / i;
            }
        }
    }
}

3)博弈论

简单博弈

/*
小明经常玩 LOL 游戏上瘾,一次他想挑战K大师,不料K大师说:
“我们先来玩个空格填字母的游戏,要是你不能赢我,就再别玩LOL了”。
K大师在纸上画了一行n个格子,要小明和他交替往其中填入字母。

并且:
1. 轮到某人填的时候,只能在某个空格中填入L或O
2. 谁先让字母组成了“LOL”的字样,谁获胜。
3. 如果所有格子都填满了,仍无法组成LOL,则平局。

小明试验了几次都输了,他很惭愧,希望你能用计算机帮他解开这个谜。
 */

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

public class Main {
    
    static Map<String, Integer> map = new HashMap<>();//记忆话搜索,记录已经走过的字符串及胜负情况
    
    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(in.readLine());
        for(int i = 0; i < n; i++) {
            String s = in.readLine();
            System.out.println(game(s));
        }
    }

    private static int game(String s) {
        map.clear();
        return f(s.toCharArray());
    }

    private static int f(char[] c) {
        String s = new String(c);
        if(map.get(s) != null) {
            return map.get(s);
        }
        if(s.contains("LOL")) {
            map.put(s, -1);
            return -1;
        }
        if(!s.contains("*")) {
            map.put(s, 0);
            return 0;
        }
        boolean ping = false;
        for(int i = 0; i < c.length; i++) {
            if(c[i] == '*') {
                try {
                    c[i] = 'L';
                    {
                        int k = f(c);
                        if(k == -1) {
                            map.put(s, 1);
                            return 1;
                        }
                        if(k == 0) {
                            ping = true;
                        }
                    }
                    c[i] = 'O';
                    {
                        int k = f(c);
                        if(k == -1) {
                            map.put(s, 1);
                            return 1;
                        }
                        if(k == 0) {
                            ping = true;
                        }
                    }
                }finally {
                    c[i] = '*';
                }
            }
        }
        if(ping) {
            map.put(s, 0);
            return 0;
        }
        map.put(s, -1);
        return -1;
    }
}

NIM堆问题

/*
有3堆硬币,分别是3,4,5
二人轮流取硬币。
每人每次只能从某一堆上取任意数量。
不能弃权。
取到最后一枚硬币的为赢家。

求先取硬币一方有无必胜的招法。
*/
static void f(int[] a){
    int sum = 0;
    for(int i = 0; i < a.length; i++){
        sum ^= a[i];
    }
    if(sum == 0){
        System.out.println("输了");
            return;
    }
    //打印必胜的方法
    for(int i=0; i<a.length; i++){
        int x = sum ^ a[i];
        if(x<a[i]) System.out.println(a[i] + " --> " + x);
    }
}

4)排列组合

全排列(0 ~ 9)

static int[] a = new int[10];
static boolean[] vis = new boolean[10];

public static void dfs(int step){
    if(step == 10){
        for(int i = 0; i < 10; i++){
            System.out.println(a[i] + " ");
        }
        return;
    }
    for(int i = 0; i < 10; i++){
        if(!vis[i]){
            vis[i] = true;
            a[step] = i;
            dfs(step + 1);
            vis[i] = false;
        }
    }
}

不重复全排列

static int[] a = {1, 1, 2};

public static void dfs(int k){
    if(k == a.length){
        for(int i = 0; i < a.length; i++){
            System.out.println(a[i] + " ");
        }
        return;
    }
    for(int i = k; i < a.length; i++){
        if(needSwap(k, i)){
            swap(i, k);
            dfs(step + 1);
            swap(i, k);
        }
    }
}

public static boolean needSwap(int from, int to) {
    for(int i = from; i < to; i++) {
        if(a[to] == a[i]) {
            return false;
        }
    }
    return true;
}

public static void swap(int x, int y) {
    int temp = a[x];
    a[x] = a[y];
    a[y] = temp;
}

不重复一般组合(例如,从4个中选3个所有组合)

static int[] a = {1, 1, 2, 4};

public static void select_combination(int k, int n){
    if(k == n){
        for(int i = 0; i < n; i++){
            System.out.println(a[i] + " ");
        }
        return;
    }
    for(int i = k; i < a.length; i++){
        if(needSwap(k, i)){
            swap(i, k);
            dfs(step + 1);
            swap(i, k);
        }
    }
}

public static boolean needSwap(int from, int to) {
    for(int i = from; i < to; i++) {
        if(a[to] == a[i]) {
            return false;
        }
    }
    return true;
}

public static void swap(int x, int y) {
    int temp = a[x];
    a[x] = a[y];
    a[y] = temp;
}

全组合(列出数列的全部子集):利用了状态压缩

public static void full_combination(int[] a) {
    int len = a.length;
    for(int i = 1; i < (1 << len); i++) {
        for(int j = 0; j < len; j++) {
            if((i & (1 << j)) > 0) {
                System.out.print(a[j] + " ");
            }
        }
        System.out.println();
    }
}

5)快速幂

a ^ b

public static long pow(int a, int b){
    int res = 1;
    while(b > 0){
        if((b & 1) == 1){
            res *= a;
        }
        b >>= 1;
        a *= a;
    }
    return res;
}

矩阵快速幂

static class Matrix{
    int n;
    int a[][] = new int[n][n];
    public Matrix(){}
    public Matrix(int n){
        this.n = n;
    }
}

public static Matrix matrix_pow(A, p, mod){
    Matrix B = unit(A.n);
    while(p > 0){
        if((p & 1) == 1){
            B = matrix_mult(B, A, mod);
        }
        p >>= 1;
        A = matrix_mult(A, A, mod);
    }
    return B;
}

public static Matrix unit(int n){
    Matrix B = new Matrix(n);
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < n; j++) {
            if(i == j) {
                B.a[i][j] = 1;
            }else {
                B.a[i][j] = 0;
            }
        }
    }
    return B;
}

public static Matrix matrix_mult(Matrix A, Matrix B, int mod) {
    Matrix C = new Matrix(A.n, B.n);
    for(int i = 0; i < A.n; i++) {
        for(int j = 0; j < B.n; j++) {
            C.a[i][j] = 0;
            for(int k = 0; k < A.n; k++) {
                C.a[i][j] = C.a[i][j] + (A.a[i][k] * B.a[k][j] % mod);
            }
        }
    }
    return C;
}

public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    int n = in.nextInt();
    int p = in.nextInt();
    int mod = in.nextInt();
    Matrix A = new Matrix(n);
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < n; j++) {
            A.a[i][j] = in.nextInt();
        }
    }
    Matrix res = matrix_pow(A, p, mod);
    for(int i = 0; i < res.n; i++) {
        for(int j = 0; j < res.n; j++) {
            System.out.print(res.a[i][j] + " ");
        }
        System.out.println();
    }
}

6)循环节长度(a/b)

public static int f(int n, int m) {
    n = n % m;
    Vector<Integer> v = new Vector<>();
    while(true) {
        v.add(n);
        n *= 10;
        n = n % m;
        if (n == 0)
            return 0;
        if (v.indexOf(n) >= 0)
            return v.size() - v.indexOf(n);
    }
}

二、字符串(学的不多)

编辑距离

/*
编辑距离,⼜又称Levenshtein距离(也叫做Edit Distance),是指两个字串串之间,由一个转成 另一个所需的少编辑操作次数。许可的编辑操作包括将⼀一个字符替换成另一个字符,插⼊一个字 符,删除⼀个字符
*/

public static void main(String[] args){
    Scanner in = new Scanner(System.in);
    String s1 = in.nextLine();
    String s2 = in.nextLine();
    char[] c1 = s1.toCharArray();
    char[] c2 = s2.toCharArray();
    int len1 = c1.length;
    int len2 = c2.length;
    int[][] dp = new int[len1 + 1][len2 + 1];
    for(int i = 1; i <= len1; i++){
        dp[i][0] = i;
    }
    for(int i = 1; i <= len2; i++){
        dp[0][i] = i;
    }
    for(int i = 1; i <= len1; i++){
        for(int j = 1; j <= len2; j++){
            if(c[i - 1] == c[j - 1]){
                dp[i][j] = dp[i - 1][j - 1];
            }else{
                dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;
            }
        }
    }
    System.out.println(dp[len1][len2]);
}

最长公共子序列

/*
比如字符串1:BDCABA;字符串2:ABCBDAB 最长公共子序列是:BCBA
*/
public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    String s = in.nextLine();
    char[] c1 = s.toCharArray();
    s = in.nextLine();
    char[] c2 = s.toCharArray();
    int len1 = c1.length;
    int len2 = c2.length;
    int dp[][] = new int[len1 + 1][len2 + 1];
    for(int i = 1; i <= len1; i++) {
        for(int j = 1; j <= len2; j++) {
            if(c1[i - 1] == c2[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
            }else {
                dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
    }
    System.out.println(dp[len1][len2]);
}

最长上升子序列

/*
比如:2 5 3 4 1 7 6  其中最长上升子序列是 2 3 4 7 
*/
public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    int n = in.nextInt();
    int[] a = new int[n];
    for(int i = 0; i < n; i++) {
        a[i] = in.nextInt();
    }
    int dp[] = new int[n];
    int ans = 1;
    for(int i = 0; i < n; i++) {
        dp[i] = 1;
        for(int j = i; j >= 0; j--) {
            if(dp[j] < dp[i]) {
                if(a[j] < a[i]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
        }
        ans = Math.max(dp[i], ans);
    }
    System.out.println(ans);
}

Karp-Rabin算法 (字符串匹配)

/**
比如:字符串1:abcaabcaabcbcabc 字符串2:abc 答案:4
*/
static int send = 31;

public static void karp_rabin(String s1, String s2){
    long hash_s2 = hash(s2);
    int len_s1 = s1.length();
    int len_s2 = s2.length();
    long[] hash_s1 = new long[len_s1 - len_s2 + 1];
    hash_s1[0] = hash(s1.subString(0, len_s2));
    int ans = 0;
    for(int i = len_s2; i < len_s1; i++){
        char newChar = s1.charAt(i);
        char oldChar = s1.charAt(i - len_s2);
        long v = (long) (hash_s1[i - len_s2] * send - oldChar * Math.pow(send, len_s2) + newChar);
        hash_s1[i - len_s2 + 1] = v;
    }
    for(int i = 0; i < hash_s1.length; i++) {
        if (hash_s1[i] == hash_s2) { // 当j == len_s2的时候则说明第二个循环从头扫到尾,即满足题意。
            ans++;
            // 为了验证是否正确,进行了下标打印(下标0代表字符串的第一位)
            System.out.println("match:" + i);
            System.out.println(ans);
        }
    }
}

public static long hash(String s){
    int len = s.length();
    long hash = 0;
    for(int i = 0; i < len; i++){
        hash = send * hash + s.charAt(i);
    }
    return hash;
}

KMP算法(字符串匹配)

public static int[] solve_next(String s){
    int len = s.length();
    int[] next = new int[len];
    next[0] = -1;
    if(len == 1){
        return next;
    }
    next[1] = 0;
    int j = 1;
    int k = next[j];
    while(j < len - 1){
        if(k < 0 || s.charAt(j) == s.charAt(k)){
            next[++j] = ++k;
        }else{
            k = next[k];
        }
    }
    return next;
}

public static int kmp(String s1, String s2){
    int len_s1 = s1.length();
    int len_s2 = s2.length();
    int next[] = solve_next(s2);
    int i = 0;
    int j = 0;
    int count = 0;
    while(i < len_s1){
        if(j == -1 || s1.charAt(i) == s2.charAt(j)){
            i++;
            j++;
        }else{
            j = next[j];
        }
        if(j == len_s2){
            count++;
            i--;
            j = next[j - 1];
        }
    }
    return count;
}

三、背包问题

01背包

public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    int N = in.nextInt();
    int V = in.nextInt();
    int[] c = new int[N + 1];
    int[] w = new int[N + 1];
    for(int i = 1; i <= N; i++) {
        w[i] = in.nextInt();
        c[i] = in.nextInt();
    }
    int[] dp = new int[V + 1];
    for(int i = 1; i <= N; i++) {
        for(int j = V; j >= c[i]; j--) {
            dp[j] = Math.max(dp[j - c[i]] + w[i], dp[j]);
        }
    }
    System.out.println(dp[V]);
}

多重背包

public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    int N = in.nextInt();
    int V = in.nextInt();
    int[] c = new int[N + 1];
    int[] w = new int[N + 1];
    int[] n = new int[N + 1];
    for(int i = 1; i <= N; i++) {
        w[i] = in.nextInt();
        c[i] = in.nextInt();
        n[i] = in.nextInt();
    }
    int[] dp = new int[V + 1];
    for(int i = 1; i <= N; i++) {
        for(int j = V; j >= 0; j--) {
            for(int k = 0; k <= n[i]; k++){
                if(j >= k * c[i]){
                    dp[j] = Math.max(dp[j - c[i] * k] + k * w[i], dp[j]);
                }
            }
        }
    }
    System.out.println(dp[V]);
}

多重背包(二进制优化)

public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    int N = in.nextInt();
    int V = in.nextInt();
    int[] c = new int[N + 1];
    int[] w = new int[N + 1];
    int[] n = new int[N + 1];
    for(int i = 1; i <= N; i++) {
        w[i] = in.nextInt();
        c[i] = in.nextInt();
        n[i] = in.nextInt();
    }
    int[] dp = new int[V + 1];
    for(int i = 1; i <= N; i++) {
        int t = n[i];
        int k = 1;
        while(k < t){
            for(int j = V; j >= k * c[i]; j--) {
                dp[j] = Math.max(dp[j], dp[j - k * c[i]] + k * w[i]);
            }
            k *= 2;
            t -= k;
        }
        for(int j = V; j >= t * c[i]; j--) {
            dp[j] = Math.max(dp[j], dp[j - t * c[i]] + t * w[i]);
        }
    }
    System.out.println(dp[V]);
}

完全背包

public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    int N = in.nextInt();
    int V = in.nextInt();
    int[] c = new int[N + 1];
    int[] w = new int[N + 1];
    for(int i = 1; i <= N; i++) {
        w[i] = in.nextInt();
        c[i] = in.nextInt();
    }
    int[] dp = new int[V + 1];
    for(int i = 1; i <= N; i++) {
        for(int j = c[i]; j <= V; j++) {
            dp[j] = Math.max(dp[j - c[i]] + w[i], dp[j]);
        }
    }
    System.out.println(dp[V]);
}

混合背包

/*
题目链接:https://www.acwing.com/problem/content/7/
*/
import java.util.Scanner;

public class 背包问题_混合背包问题 {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int N = in.nextInt();
        int V = in.nextInt();
        int[] v = new int[N + 1];
        int[] w = new int[N + 1];
        int[] s = new int[N + 1];
        for(int i = 1; i <= N; i++) {
            v[i] = in.nextInt();
            w[i] = in.nextInt();
            s[i] = in.nextInt();
        }
        int[] dp = new int[V + 1];
        for(int i = 1; i <= N; i++) {
            if(s[i] == -1) {
                for(int j = V; j >= v[i]; j--) {
                    dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]);
                }
            }else if(s[i] == 0) {
                for(int j = v[i]; j <= V; j++) {
                    dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]);
                }
            }else {
                int k = 1;
                int t = s[i];
                while(k < t) {
                    for(int j = V; j >= k * v[i]; j--) {
                        dp[j] = Math.max(dp[j], dp[j - k * v[i]] + k * w[i]);
                    }
                    t -= k;
                    k <<= 1;
                }
                for(int j = V; j >= t * v[i]; j--) {
                    dp[j] = Math.max(dp[j], dp[j - t * v[i]] + t * w[i]);
                }
            }
        }
        System.out.println(dp[V]);
    }
}

二维费用背包问题

/*
题目链接:https://www.acwing.com/problem/content/8/
*/
import java.util.Scanner;

public class 背包问题_二维费用背包问题 {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int N = in.nextInt();
        int V = in.nextInt();
        int M = in.nextInt();
        int[] v = new int[N + 1];
        int[] m = new int[N + 1];
        int[] w = new int[N + 1];
        for(int i = 1; i <= N; i++) {
            v[i] = in.nextInt();
            m[i] = in.nextInt();
            w[i] = in.nextInt();
        }
        int[][] dp = new int[V + 1][M + 1];
        for(int i = 1; i <= N; i++) {
            for(int j = V; j >= v[i]; j--) {
                for(int k = M; k >= m[i]; k--) {
                    dp[j][k] = Math.max(dp[j][k], dp[j - v[i]][k - m[i]] + w[i]);
                }
            }
        }
        System.out.println(dp[V][M]);
    }
}

组合背包问题

/*
题目链接:https://www.acwing.com/problem/content/9/
*/
import java.util.Scanner;

public class 背包问题_分组背包问题 {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int N = in.nextInt();
        int V = in.nextInt();
        int[] a = new int[N + 1];
        int[][] v = new int[N + 1][];
        int[][] w = new int[N + 1][];
        for(int i = 1; i <= N; i++) {
            a[i] = in.nextInt();
            v[i] = new int[105];
            w[i] = new int[105];
            for(int j = 1; j <= a[i]; j++) {
                v[i][j] = in.nextInt();
                w[i][j] = in.nextInt();
            }
        }
        int[] dp = new int[V + 1];
        for(int i = 1; i <= N; i++) {
            for(int j = V; j >= 0; j--) {
                for(int k = 1; k <= a[i]; k++) {
                    if(j >= v[i][k]){
                        dp[j] = Math.max(dp[j], dp[j - v[i][k]] + w[i][k]);
                    }
                }
            }
        }
        System.out.println(dp[V]);
    }
}

三、图论

Dijkstra(邻接矩阵形式)——求单源最短路(无负权边)

import java.util.Arrays;
import java.util.Scanner;

public class 图_最短路径_dijkstra_邻接矩阵 {
    
    private static int n, m;
    private static int[][] map;
    private static boolean[] vis;
    private static int[] dis;
    private static final int INF = 0x3f3f3f;
    
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        m = in.nextInt();
        init();//初始化
        for(int i = 0; i < m; i++) {
            int a = in.nextInt();//起点
            int b = in.nextInt();//终点
            int c = in.nextInt();//权值
            insert(a, b, c);//无向图
//          insert01(a, b, c);//有向图
        }
        dijkstra(1);
        System.out.println(dis[n]);
    }

    private static void init() {
        map = new int[n + 1][n + 1];
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= n; j++) {
                if(i == j) map[i][j] = 0;
                else map[i][j] = INF;
            }
        }
        vis = new boolean[n + 1];
        dis = new int[n + 1];
        Arrays.fill(dis, INF);
    }

    private static void dijkstra(int u) {
        for(int i = 1; i <= n; i++){
            dis[i] = map[u][i];
        }
        vis[i] = true;
        for(int i = 1; i <= n - 1; j++){
            int mind = INF;
            int minj = -1;
            for(int j = 1; j <= n; j++){
                if(!vis[j] && dis[j] < mind){
                    mind = dis[j];
                    minj = j;
                }
            }
            if(minj == -1) return;
            vis[minj] = true;
            for(int j = 1; j <= n; j++){
                if(map[minj][j] < INF){
                    if(dis[j] > dis[minj] + map[minj][j]){
                        dis[j] = dis[minj] + map[minj][j];
                    }
                }
            }
        }
    }

    //无向图添加边
    private static void insert(int a, int b, int c) {
        map[a][b] = c;
        map[b][a] = c;
    }
    
    //有向图添加边
    private static void insert(int a, int b, int c) {
        map[a][b] = c;
    }

}

Dijkstra(邻接矩阵链表)——求单源最短路(无负权边)

import java.util.Arrays;
import java.util.Scanner;

public class 图_最短路径_dijkstra_邻接链表 {
    
    private static int n, m;
    private static boolean[] vis;
    private static int[] dis;
    private static final int INF = 0x3f3f3f;
    private static Edge[] e;
    private static int[] head;
    private static int size;
    
    static class Edge{
        int v;//终点
        int w;//权重
        int next;//下一条边的id
        public Edge(){}
        public Edge(int v, int w, int next){
            this.v = v;
            this.w = w;
            this.next = next;
        }
    }
    
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        m = in.nextInt();
        init();//初始化
        for(int i = 0; i < m; i++) {
            int a = in.nextInt();//起点
            int b = in.nextInt();//终点
            int c = in.nextInt();//权值
            insert(a, b, c);//无向图
//          insert01(a, b, c);//有向图
        }
        dijkstra(1);
        System.out.println(dis[n]);
    }

    private static void init() {
        e = new Edge[2 * m + 1];//无向图
//      e = new Edge[m + 1];//有向图
        vis = new boolean[n + 1];
        dis = new int[n + 1];
        Arrays.fill(dis, INF);
        head = new int[n + 1];
        Arrays.fill(head, -1);
    }

    private static void dijkstra(int u) {
        dis[u] = 0;
        for(int i = 1; i <= n; i++){
            int mind = INF;
            int minj = -1;
            for(int j = 1; j <= n; j++){
                if(!vis[j] && dis[j] < mind){
                    mind = dis[j];
                    minj = j;
                }
            }
            if(minj == -1){
                return;
            }
            vis[minj] = true;
            for(int j = head[minj]; j != -1; j = e[j].next){
                int v = e[j].v;
                int w = e[j].w;
                if(!vis[v] && dis[v] > dis[minj] + w){
                    dis[v] = dis[minj] + w;
                }
            }
        }
    }

    //无向图添加边
    private static void insert(int u, int v, int w) {
        e[size] = new Edge(v, w, head[u]);
        head[u] = size++;
        e[size] = new Edge(u, w, head[v]);
        head[v] = size++;
    }
    
    //有向图添加边
    private static void insert(int u, int v, int w) {
        e[size] = new Edge(b, c, head[a]);
        head[u] = size++;
    }

}

SPFA(邻接链表)——单源最短路(有负权边)

import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class 图_最短路径_spfa_邻接链表 {
    
    private static int n, m, size;
    private static Edge[] e;
    private static int[] head;
    private static int[] dis;
    private static boolean[] vis;
    private static final int INF = 0x3f3f3f;
    private static int[] in;
    
    public static class Edge{
        int v;
        int w;
        int next;
        public Edge() {}
        public Edge(int v, int w, int next) {
            this.v = v;
            this.w = w;
            this.next = next;
        }
    }
    
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        m = in.nextInt();
        init();
        for(int i = 0; i < m; i++) {
            int a = in.nextInt();
            int b = in.nextInt();
            int c = in.nextInt();
            insert02(a, b, c);
        }
        if(spfa(1)) {
            System.out.println("有负环");
        }else {
            System.out.println(dis[n]);
        }
    }

    //加入负环判断
    private static boolean spfa(int u) {
        vis[u] = true;
        dis[u] = 0;
        in[u]++;
        Queue<Integer> q = new LinkedList<>();
        q.add(u);
        while(!q.isEmpty()){
            int now = q.poll();
            vis[now] = false;
            for(int i = head[now]; i != -1; i = e[i].next){
                int v = e[i].v;
                int w = e[i].w;
                if(dis[v] > dis[u] + w){
                    dis[v] = dis[u] + w;
                    if(!vis[v]){
                        q.add(v);
                        vis[v] = true;
                        in[v]++;
                        if(in[v] > n) return true;
                    }
                }
            }
        }
        return false;
    }

    private static void init() {
        e = new Edge[2 * m + 1];
        head = new int[n + 1];
        Arrays.fill(head, -1);
        vis = new boolean[n + 1];
        dis = new int[n + 1];
        Arrays.fill(dis, INF);
        in = new int[n + 1];
    }

    private static void insert01(int u, int v, int w) {
        e[size] = new Edge(v, w, head[u]);
        head[u] = size++;
    }
    
    private static void insert02(int u, int v, int w) {
        insert01(u, v, w);
        insert01(v, u, w);
    }
}

floyd——多源最短路

public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    int n = in.nextInt();
    int m = in.nextInt();
    int[][] map = new int[n + 1][n + 1];
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            if(i == j) map[i][j] = 0;
            else map[i][j] = 0x3f;
        }
    }
    for(int i = 0; i < m; i++) {
        int a = in.nextInt();
        int b = in.nextInt();
        int c = in.nextInt();
        map[a][b] = map[b][a] = c;
    }
    //floyd核心
    for(int k = 1; k <= n; k++){
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                map[i][j] = Math.min(map[i][j], map[i][k] + map[k][j]);
            }
        }
    }
    System.out.println(map[1][n]);
}

图的拓扑排序

public class 图_拓扑排序 {
    
    private static int n, m;
    private static Edge[] e;
    private static int[] indegree;
    private static int[] head;
    private static int size;
    
    public static class Edge{
        int v;
        int next;
        public Edge() {}
        public Edge(int v, int next) {
            this.v = v;
            this.next = next;
        }
    }
    
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        m = in.nextInt();
        init();
        for(int i = 0; i < m; i++) {
            int a = in.nextInt();
            int b = in.nextInt();
            insert(a, b);
            indegree[b]++;
        }
        topo();
    }

    private static void topo() {
        Queue<Integer> q = new LinkedList<>();
        for(int i = 1; i <= n; i++) {
            if(indegree[i] == 0) {
                q.add(i);
            }
        }
        while(!q.isEmpty()) {
            int now = q.peek();
            System.out.println(now);
            q.poll();
            for(int i = head[now]; i != -1; i = e[i].next) {
                int v = e[i].v;
                indegree[v]--;
                if(indegree[v] == 0) {
                    q.add(v);
                }
            }
        }
    }

    private static void insert(int u, int v) {
        e[size] = new Edge(v, head[u]);
        head[u] = size++;
    }

    private static void init() {
        e = new Edge[m + 1];
        indegree = new int[n + 1];
        Arrays.fill(indegree, 0);
        head = new int[2 * m + 1];
        Arrays.fill(head, -1);
    }
}

最小生成树——Kruskal

public class 最小生成树 {
    
    static int n, m;
    static Edge[] e;
    static int[] pre;
    
    public static class Edge{
        int u;
        int v;
        int w;
        public Edge() {}
        public Edge(int u, int v, int w) {
            this.u = u;
            this.v = v;
            this.w = w;
        }
        @Override
        public String toString() {
            return "Edge [u=" + u + ", v=" + v + ", w=" + w + "]";
        }
    }
    
    public static int krusual(){
        init();
        Arrays.sort(e, new Comparator<Edge>() {
            public int compare(Edge o1, Edge o2) {
                return o1.w - o2.w;
            }
        });
        int count = 0;//记录已生成的边数
        int sum = 0;//记录答案
        for(int i = 0; i < m; i++) {
            if(merge(e[i].u, e[i].v)) {
                count++;
                sum += e[i].w;
            }
            if(count == n - 1) {//满足最小生成树条件
                break;
            }
        }
        if(count < n - 1) {
            return -1;
        }else{
            return sum;
        }
    }
    
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        m = in.nextInt();
        e = new Edge[m];
        for(int i = 0; i < m; i++) {
            e[i] = new Edge(in.nextInt(), in.nextInt(), in.nextInt());
        }
        int res = krusual();
        if(res == -1){
            System.out.println("不连通);
        }else{
            System.out.println(res);
        }
    }

    private static boolean merge(int u, int v) {
        int uu = find(u);
        int vv = find(v);
        if(uu != vv){
            pre[vv] == uu;
            return true;
        }
        return false;
    }

    private static int find(int u) {
        int r = u;
        while(r != pre[r]){
            r = pre[r];
        }
        return r;
    }

    //并查集初始
    private static void init() {
        pre = new int[n + 1];
        for(int i = 1; i <= n; i++) {
            pre[i] = i;
        }
    }
}

最小生成树——Prim

import java.util.Arrays;
import java.util.Scanner;

public class 最小生成树_Prim {
    
    static int n, m;
    static final int INF = 0x3f3f3f;
    static int[] head;
    static int size;
    static Edge[] e;
    static boolean[] vis;
    static int[] dis;
    
    static class Edge{
        int v;
        int w;
        int next;
        public Edge() {}
        public Edge(int v, int w, int next) {
            this.v = v;
            this.w = w;
            this.next = next;
        }
    }
    
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        m = in.nextInt();
        init();
        int st = 0;
        for(int i = 0; i < m; i++) {
            int u = in.nextInt();
            int v = in.nextInt();
            int w = in.nextInt();
            insert(u, v, w);
            insert(v, u, w);
            st = u;
        }
        int res = prim(st);
        System.out.println(res);
    }

    private static int prim(int st) {
        int sum = 0;
        int count = 0;
        dis[st] = 0;
        for(int i = head[st]; i != -1; i = e[i].next) {
            int v = e[i].v;
            int w = e[i].w;
            if(dis[v] > w) {
                dis[v] = w;
            }
        }
        vis[st] = true;
        count++;
        while(count < n) {
            int mind = INF;
            int minj = -1;
            for(int i = 1; i <= n; i++) {
                if(!vis[i] && dis[i] < mind) {
                    mind = dis[i];
                    minj = i;
                }
            }
            vis[minj] = true;
            count++;
            sum += mind;
            for(int i = head[minj]; i != -1; i = e[i].next) {
                int v = e[i].v;
                int w = e[i].w;
                if(!vis[v] && dis[v] > w) {
                    dis[v] = w;
                }
            }
        }
        return sum;
    }

    private static void init() {
        head = new int[n + 1];
        Arrays.fill(head, -1);
        e = new Edge[m * 2 + 1];
        vis = new boolean[n + 1];
        dis = new int[n + 1];
        Arrays.fill(dis, INF);
    }

    private static void insert(int u, int v, int w) {
        e[size] = new Edge(v, w, head[u]);
        head[u] = size++;
    }
}

线段树(多用于解决区间问题)

import java.util.Arrays;
import java.util.Scanner;

public class 线段树_构建 {
    
    private static final int MAX = 105;
    private static int[] a = new int[MAX];
    private static int[] minv = new int[MAX * 4];
    
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        for(int i = 1; i <= n; i++) {
            a[i] = in.nextInt();
        }
        build_tree(1, 1, n);
        update_tree(1, 1, n, 5, 7);//更新线段树中a数组下标为5的值。
        query_tree(1, 1, n, 2, 5);//查询2~5区间的最小值
    }


    //区间查询
    private static int query_tree(int id, int l, int r, int x, int y) {
        if(x <= l && r <= y) {
            return minv[id];
        }
        int mid = (l + r) >> 1;
        int res = Integer.MAX_VALUE;
        if(x <= mid) {
            res = Math.min(res, query_tree(id << 1, l, mid, x, y));
        }
        if(y > mid) {
            res = Math.min(res, query_tree(id << 1 | 1, mid + 1, r, x, y));
        }
        return res;
    }

    //更新
    private static void update_tree(int id, int l, int r, int x, int v) {
        if(l == r) {
            minv[id] = v;
            return;
        }
        int mid = (l + r) >> 1;
        if(x <= mid) {
            update_tree(id << 1, l, mid, x, v);
        }else {
            update_tree(id << 1 | 1, mid + 1, r, x, v);
        }
        minv[id] = Math.min(minv[id << 1], minv[id << 1 | 1]);
    }

    //构建线段树
    private static void build_tree(int id, int l, int r) {
        if(l == r) {
            minv[id] = a[l];
            return;
        }
        int mid = (l + r) >> 1;
        build_tree(id << 1, l, mid);
        build_tree(id << 1 | 1, mid + 1, r);
        minv[id] = Math.min(minv[id << 1], minv[id << 1 | 1]);
    }
}

四、二分

二分

private static int binary_search(Integer[] a, int k) {
    int l = 0;
    int r = a.length - 1;
    while(l <= r) {
        int mid = (r + l) >> 1;
        if(a[mid] == k) return mid;
        if(a[mid] < k) l = mid + 1;
        else r = mid - 1;
    }
    return -1;
}

最大值最小问题

public class 二分_最大值最小 {
    
    static int[] a = {2, 4, 6, 8, 10, 12, 14};
    
    public static void main(String[] args) {
        System.out.println(bsearch_maxMin(11));
    }

    private static int bsearch_maxMin(int x) {
        int l = 0;
        int r = a.length - 1;
        while(l < r) {
            int mid = (l + r) >> 1;
            if(x <= a[mid]) {
                r = mid;
            }else {
                l = mid + 1;
            }
        }
        return l;//返回5
    }
}

最小值最大问题

public class 二分_最小值最大 {
    
    static int[] a = {2, 4, 6, 8, 10, 12, 14};
    
    public static void main(String[] args) {
        System.out.println(bsearch_maxMin(3));
    }

    private static int bsearch_maxMin(int x) {
        int l = 0;
        int r = a.length - 1;
        while(l < r) {
            int mid = (l + r + 1) >> 1;
            if(x <= a[mid]) {
                r = mid - 1;
            }else {
                l = mid;
            }
        }
        return l;//返回4
    }
}
上一篇下一篇

猜你喜欢

热点阅读