acwing-Java模板

单链表

826. 单链表

import java.util.Scanner;

public class Main {
    static int N = 100010;
    static int[] e = new int[N];
    static int[] ne = new int[N];
    static int head = -1;
    static int idx = 0;
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        while(n-- > 0){
            String str = in.next();
            if(str.equals("H")) {
                int x = in.nextInt();
                insertHead(x);
            }else if(str.equals("I")){
                int k = in.nextInt();
                int x = in.nextInt();
                insert(k-1, x);
            }else if(str.equals("D")){
                int k = in.nextInt();
                if(k == 0){
                    head = ne[head];
                }else{
                    delete(k-1);
                }
            }
        }
        for(int i = head; i != -1; i = ne[i]){
            System.out.print(e[i] + " ");
        }
    }
    private static void insertHead(int x){
        e[idx] = x;
        ne[idx] = head;
        head = idx;
        idx++;
    }
    private static void insert(int k, int x) {
        e[idx] = x;
        ne[idx] = ne[k];
        ne[k] = idx;
        idx++;
    }
    private static void delete(int k){
        ne[k] = ne[ne[k]];
    }
}

单调栈

830. 单调栈

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[] stack = new int[100010];
        int top = 0;
        for(int i = 0; i < n; i++){
            int x = in.nextInt();
            while(top != 0 && stack[top] >= x){
                top--;
            }
            //如果弹出操作完了之后,栈不是空的,就输出栈顶元素,
            if(top != 0){
                System.out.print(stack[top] + " ");
            }
            else System.out.println(-1 + " ");
            stack[++top] = x;
        }
    }
}

单调队列

import java.util.Scanner;

public class Main {
    static int N = 100010;
    static int head, tail;
    static int[] nums = new int[N], queue = new int[N];
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int k = in.nextInt();
        for(int i = 0; i < n; i++){
            nums[i] = in.nextInt();
        }
        head = 0;
        tail = -1;
        for(int i = 0; i < n; i++){
            if(head <= tail && queue[head] < i - k + 1) head++; //queue[head]:当前元素下标,超过队列长度,丢弃
            while(head <= tail && nums[queue[tail]] >= nums[i]) tail--; //队列末尾元素>=当前元素,丢弃末尾元素
            queue[++tail] = i;
            if(i >= k-1){
                System.out.print(nums[queue[head]] + " ");
            }
        }
        System.out.println();
        head = 0;
        tail = -1;
        for(int i = 0; i < n; i++){
            if(head <= tail && queue[head] < i - k + 1) head++; //queue[head]:当前元素下标,超过队列长度,丢弃
            while(head <= tail && nums[queue[tail]] <= nums[i]) tail--; //队列末尾元素>=当前元素,丢弃末尾元素
            queue[++tail] = i;
            if(i >= k-1){
                System.out.print(nums[queue[head]] + " ");
            }
        }
    }
}

并查集

  1. 将两个集合合并
  2. 询问两个元素是否在一个集合当中

每个集合用一颗树表示。树根的编号就是整个集合的编号。每个节点存储它的父节点,p[x]表示x的父节点

  • 如何判断树根:if(p[x] == x)
  • 如何求x的集合编号:while(p[x] != x) x = p[x];路径压缩优化:
  • 如何合并两个集合:p[x] = y

合并集合

import java.util.Scanner;

public class Main {
    static int N = 100010;
    static int[] p = new int[N];
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        for(int i = 1; i <= n; i++){
            p[i] = i;   // 让每个元素的父亲是自己
        }
        while(m-- > 0){
            String str = in.next();
            int a = in.nextInt();
            int b = in.nextInt();
            if(str.equals("M")){
                p[find(a)] = find(b);//合并集合:将a集合的根节点即祖先指向b集合的祖先
            }else{
                if(find(a) == find(b)){
                    System.out.println("Yes");
                }else{
                    System.out.println("No");
                }
            }
        }
    }
    private static int find(int x){//寻找根节点祖先 + 路径压缩
        if(p[x] != x){
            p[x] = find(p[x]);
        }
        return p[x];
    }
}

837. 连通块中点的数量

import java.util.Scanner;

public class Main {
    static int N = 100010;
    static int[] p = new int[N];
    static int[] size = new int[N]; //size用来存每个集合中数的个数
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        for(int i = 1; i <= n; i++){
            p[i] = i;   // 让每个元素的父亲是自己
            size[i] = 1;
        }
        while(m-- > 0){
            String s = in.next();
            if(s.equals("C")){
                int a = in.nextInt();
                int b = in.nextInt();
                if(find(a) == find(b)) continue;
                p[find(a)] = find(b);
                size[find(b)] += size[find(a)];	
            }else if(s.equals("Q1")){
                int a = in.nextInt();
                int b = in.nextInt();
                if(find(a) == find(b)) System.out.println("Yes");
                else System.out.println("No");
            }else{
                int a = in.nextInt();
                System.out.println(size[find(a)]);
            }
        }
    }
    private static int find(int x){//寻找根节点祖先 + 路径压缩
        if(p[x] != x){
            p[x] = find(p[x]);
        }
        return p[x];
    }
}

  1. 插入一个数:heap[++size] = x;up(size);
  2. 求集合当中的最小值:heap[1];
  3. 删除最小值:heap[1] = heap[size]; size–;down(1)
  4. 删除任意一个元素:heap[k] = heap[size]; size–;down(k) / up(k)
  5. 修改任意一个元素:heap[k] = x; down(k) / up(k);

838. 堆排序


哈希表

二分

//区间[l, r]被划分为[l, mid]和[mid+1, r]时使用:
private static int helper(int l, int r) {
    while(l < r){
        int mid = l + r >> 1;
        if(check(mid)) r = mid;
        else l = mid + 1;
    }
    return l;
}
//区间[l, r]被划分为[l, mid-1]和[mid, r]时使用:
//当l=r-1时,不加1,check(mid)==true会导致l=l,死循环,否则会l=r,while(r=r)结束循环。
private static int helper(int l, int r) {
    while(l < r){
        int mid = l + r + 1 >> 1;
        if(check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

789. 数的范围

import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int count = in.nextInt();
        int[] nums = new int[n];
        for(int i = 0; i < n; i++){
            nums[i] = in.nextInt();
        }
        for(int i = 0; i < count; i++){
            int target = in.nextInt();
            int l = 0, r = n - 1;
            while(l < r){//找到左边界
                int mid = l + r >> 1;
                if(nums[mid] >= target){//说明答案一定在右半边
                    r = mid;//mid也可能是答案[l, mid]
                }
                else{
                    l = mid + 1;//[mid+1, r]
                }
            }
            if(nums[l] != target){
                System.out.println("-1 -1");
            }else{
                System.out.print(l + " ");
                l = 0;
                r = n-1;
                while(l < r){//找到右边界
                    int mid = l + r + 1 >> 1;
                    if(nums[mid] <= target){//说明答案在左半边
                        l = mid;//[mid, r]
                    }else{
                        r = mid - 1;//[l, mid-1]
                    }
                }
                System.out.println(l);
            }
        }
    }
}

790. 数的三次方根

//浮点数二分
import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        double x = in.nextDouble();
        double eps = 1e-8;
        double l = -100, r = 100;//题目给定范围
        while (r - l > eps) {
            double mid = (l + r) / 2;
            if (mid * mid * mid >= x){
                r = mid;//不需要+1&-1
            }else {
                l = mid;//不需要+1&-1
            }
        }
        System.out.println(String.format("%.6f", l)); // 保留 6位小数
    }
}

前缀和&差分

795. 前缀和

//前缀和:S[i] = a[1] + a[2] + a[3] + … + a[i], 快速计算l到r区间的和
import java.util.*;
public class Main {
    static int N = 100010;
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        int[] nums = new int[N];
        for(int i = 1; i <= n; i++){
            nums[i] = in.nextInt();
        }
        int[] s = new int[N];
        for(int i = 1; i <= n; i++){
            s[i] = s[i-1] + nums[i];
        }
        for(int i = 0; i < m; i++){
            int l = in.nextInt();
            int r = in.nextInt();
            System.out.println(s[r] - s[l-1]);
        }
    }
}

796. 子矩阵的和

import java.util.*;
public class Main {
    static int N = 100010;
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int m = in.nextInt();
        int n = in.nextInt();
        int q = in.nextInt();
        int[][] arr = new int[m+1][n+1];
        for(int i = 1; i <= m; i++){
            for(int j = 1; j <= n; j++){
                arr[i][j] = in.nextInt();
            }
        }
        int[][] s = new int[m+1][n+1];
        for(int i = 1; i <= m; i++){
            for(int j = 1; j <= n; j++){
                s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + arr[i][j];//初始化前缀和
            }
        }
        for(int i = 0; i < q; i++){
            int x1 = in.nextInt();
            int y1 = in.nextInt();
            int x2 = in.nextInt();
            int y2 = in.nextInt();
            int res = s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1];//计算子和
            System.out.println(res);
        }
    }
}

AcWing 797. 差分

//差分b[N] :a[i]=b[1]+b[1]+b[3]+……+b[i], 快速得到l到r区间变化后的结果
import java.util.Scanner;

public class Main {
    static int N = 100010;
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int q = in.nextInt();
        int[] nums = new int[N];
        int[] diff = new int[N];
        for(int i = 1; i <= n; i++){
            nums[i] = in.nextInt();
        }
        for(int i = 1; i <= n; i++){
            insert(diff, i, i, nums[i]);
        }
        for (int i = 0; i < q; i++) {
            int l = in.nextInt();
            int r = in.nextInt();
            int c = in.nextInt();
            insert(diff, l, r, c);
        }
        for (int i = 1; i <= n ; i++) {
            diff[i] += diff[i-1];
            System.out.print(diff[i] + " ");
        }
    }
    private static void insert(int[] diff, int l, int r, int c){
        diff[l] += c;
        diff[r + 1] -= c;
    }
}

public class Main {
    static int N = 100010;
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int q = in.nextInt();
        int[] a = new int[N];//原数组
        int[] b = new int[N];//差分数组
        for(int i = 1; i <= n; i++){
            a[i] = in.nextInt();
        }
        //进行n次插入,初始化差分数组
        for(int i=1;i<=n;i++) {
            insert(b, i, i, a[i]);
        }
        for(int i = 0; i < q; i++){
            int l = in.nextInt();
            int r = in.nextInt();
            int c = in.nextInt();
            insert(b, l, r, c);
        }
        for(int i = 1 ; i <= n ; i ++ ) {
            b[i] += b[i - 1]; //没有a[0], a[1]=b[0]+b[1]; a[2]=b[0]+b[1]+b[2]
            System.out.print(b[i] + " ");
        }
    }
    private static void insert(int[] b, int l, int r, int c){
        b[l] += c;
        b[r + 1] -= c;
    }
}

天文爱好者

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int N = 100010;
        int n = in.nextInt();
        int[] l = new int[N];
        int[] r = new int[N];
        int[] diff = new int[N];//每分钟能观测流星数量
        for(int i = 1; i <= n; i++){
            l[i] = in.nextInt();
        }
        for (int i = 1; i <= n; i++) {
            r[i] = in.nextInt();
        }
        for(int i = 1; i <= n; i++){
            insert(diff, l[i], r[i], 1);
        }
        int max = 0;
        int count = 0;
        for (int i = 1; i < N; i++) {
            diff[i] += diff[i-1];
            if(diff[i] > max){
                max = diff[i];
                count = 1;
            }else if(diff[i] == max){
                count++;
            }
        }
        System.out.println(max + " " + count);
    }
    private static void insert(int[] diff, int l, int r, int c){
        diff[l] += c;
        diff[r+1] -= c;
    }
}

1109. 航班预订统计

class Solution {
    public int[] corpFlightBookings(int[][] bookings, int n) {
        int N = 100010;
        int[] diff = new int[N];
        int[] res = new int[n];
        int count = bookings.length;
        for(int i = 1; i <= count; i++){
            insert(diff, bookings[i-1][0], bookings[i-1][1], bookings[i-1][2]);
        }
        for(int i = 1; i <= n; i++){
            diff[i] += diff[i-1];
            res[i-1] = diff[i];
        }
        return res;
    }
    private void insert(int[] diff, int l, int r, int c){
        diff[l] += c;
        diff[r+1] -= c;
    }
}

双指针

799. 最长连续不重复子序列

import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[] nums = new int[n];
        for(int i = 0; i < n; i++){
            nums[i] = in.nextInt();
        }
        Map<Integer, Integer> map = new HashMap<>();
        int res = 0;
        for(int i = 0, j = 0; i < n; i++){
            map.put(nums[i], map.getOrDefault(nums[i], 0) + 1);
            while(map.get(nums[i]) > 1){
                map.put(nums[j], map.get(nums[j]) - 1);
                j++;
            }
            res = Math.max(res, i - j + 1);
        }
        System.out.println(res);
    }
}

位运算

AcWing 801. 二进制中1的个数

//lowbit():返回x的最后一位1(二进制表示下最低位的1以及它后面的0构成的数值)
import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        for(int i = 0; i < n; i++){
            int x = in.nextInt();
            int res = 0;
            while(x != 0){
                x -= lowbit(x);
                res++;
            }
            System.out.print(res + " ");
        }
    }
    private static int lowbit(int x){
        return x & -x;
    }
}

离散化

DFS

AcWing 842. 排列数字

import java.util.*;
public class Main {
    static List<List<Integer>> res = new ArrayList<>();
    static LinkedList<Integer> path = new LinkedList<>();
    static int n;
    static boolean[] used;
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        used = new boolean[n+1];
        dfs(0);
    }
    private static void dfs(int x){
        if(path.size() == n){
            res.add(new ArrayList<>(path));
            for(int num : path){
                System.out.print(num + " ");
            }
            System.out.println();
            return;
        }
        for(int i = 1; i <= n; i++){
            if(!used[i]){
                path.add(i);
                used[i] = true;
                dfs(x+1);
                used[i] = false;
                path.removeLast();
            }
        }
    }
}

AcWing 843. n-皇后问题

import java.util.*;
public class Main {
    static int N = 20;
    static char[][] grid;
    static boolean[] row = new boolean[N];
    static boolean[] col = new boolean[N];
    static boolean[] dg = new boolean[N];
    static boolean[] udg = new boolean[N];
    static int n;
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        grid = new char[n][n];
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                grid[i][j] = '.';
            }
        }
        dfs(0, 0, 0);
    }

    private static void dfs(int x, int y, int count) {
        if(y == n){ //走到了一行末尾,转到下一行开头
            y = 0;
            x++;
        }
        if(x == n){
            if(count == n){
                for(int i = 0; i < n; i++){
                    System.out.println(grid[i]);
                }
                System.out.println();
            }
            return;
        }
        //不放皇后
        dfs(x, y+1, count);
        //放皇后
        if(!row[x] && !col[y] && !dg[x+y] && !udg[x-y+n]){
            grid[x][y] = 'Q';
            row[x] = col[y] = dg[x+y] = udg[x-y+n] = true;
            dfs(x, y+1, count+1);
            row[x] = col[y] = dg[x+y] = udg[x-y+n] = false;
            grid[x][y] = '.';
        }
    }
}

BFS(最小最少用BFS,其余DFS)

AcWing 844. 走迷宫

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

class Point{
    int x, y;
    public Point(int x, int y){
        this.x = x;
        this.y = y;
    }
}

public class Main {
    static int m;
    static int n;
    static int[][] matrix;
    static int[][] dist;
    static int[] dx = {1, 0, -1, 0}, dy = {0, 1, 0, -1};
    static Point[][] pre;

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        m = in.nextInt();
        n = in.nextInt();
        matrix = new int[m][n];
        dist = new int[m][n];
        pre = new Point[m][n];
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                matrix[i][j] = in.nextInt();
            }
        }
        System.out.println(bfs());
    }
    private static int bfs(){
        Queue<Point> queue = new LinkedList<>();
        queue.offer(new Point(0, 0));
        while(!queue.isEmpty()){
            Point p = queue.poll();
            if(p.x == m-1 && p.y == n-1){
                break;
            }
            for(int i = 0; i < 4; i++){
                int x = p.x + dx[i];
                int y = p.y + dy[i];
                if(x >= 0 && x < m && y >= 0 && y < n && matrix[x][y] == 0 && dist[x][y] == 0){
                    queue.offer(new Point(x, y));
                    dist[x][y] = dist[p.x][p.y] + 1;
                    pre[x][y] = p;
                }
            }
        }
        int x = m-1, y = n-1;
        while(x != 0 && y != 0){
            System.out.println(x + " " + y);
            Point prevP = pre[x][y];
            x = prevP.x;
            y = prevP.y;
        }
        System.out.println(0 + " " + y);

        return dist[m-1][n-1];
    }
}

AcWing 845. 八数码

import java.util.*;

public class Main {
    static int[] dx = {-1, 0, 1, 0}, dy = {0, 1, 0, -1};
    static Map<String, Integer> map = new HashMap<>();
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        String[] s = in.nextLine().split(" ");
        String start = "";
        String end = "12345678x";
        for(int i = 0; i < s.length; i++){
            start += s[i];
        }
        System.out.println(bfs(start, end));
    }
    private static int bfs(String start, String end){
        Queue<String> queue = new LinkedList<>();
        queue.offer(start);
        map.put(start, 0);
        while(!queue.isEmpty()){
            String t = queue.poll();
            if(t.equals("12345678x")) return map.get(t);
            int idx = t.indexOf('x');
            int x = idx / 3;
            int y = idx % 3;
            for(int i = 0; i < 4; i++){
                int tx = x + dx[i];
                int ty = y + dy[i];
                if(tx >= 0 && tx < 3 && ty >= 0 && ty < 3){
                    char[] ch = t.toCharArray();
                    swap(ch, idx, tx * 3 + ty);
                    String j = new String(ch);
                    if(map.get(j) == null){
                        queue.offer(j);
                        map.put(j, map.get(t) + 1);
                    }
                }
            }
        }
        return -1;
    }
    private static void swap(char[] ch, int i, int j){
        char temp = ch[i];
        ch[i] = ch[j];
        ch[j] = temp;
    }
}

树图DFS

846. 树的重心

import java.util.*;

public class Main {
    static int N = 100010;
    static Map<Integer, List<Integer>> graph = new HashMap<>();
    static boolean[] visited = new boolean[N];
    static int ans = N;
    static int n;

    public static void add(int a, int b) {
        graph.get(a).add(b);
        graph.get(b).add(a);
    }

    public static int dfs(int node) {
        int res = 0;
        visited[node] = true;
        int sum = 1;

        for(Integer neibor : graph.get(node)){
            if(!visited[neibor]){
                int s = dfs(neibor);
                res = Math.max(res, s);//res 表示当前节点的最大子树大小,s 表示当前邻居节点的子树大小。在遍历当前节点的每个邻居节点时,需要比较邻居节点的子树大小 s 和当前节点的最大子树大小 res 的大小,将较大值赋给 res。
                sum += s;
            }
        }
        res = Math.max(res, n - sum);//表示计算当前节点的最大子树大小,其中 n - sum 表示去掉当前节点后,剩余节点的个数;
        ans = Math.min(res, ans);//表示更新最终结果 ans,使其等于所有节点的最大子树大小的最小值;
        return sum;
    }
    public static void main(String[] ags) {
        Scanner scan = new Scanner(System.in);
        n = scan.nextInt();
        for (int i = 1; i <= n; i++) {
            graph.put(i, new ArrayList<>());
        }
        for (int i = 0; i < n - 1; i++) {
            int a = scan.nextInt();
            int b = scan.nextInt();
            add(a, b);
        }
        dfs(1);
        System.out.println(ans);
    }
}

树图BFS

847. 图中点的层次

import java.lang.reflect.Array;
import java.util.*;

public class Main {
    static int n, m;
    static int N = 100010;
    static Map<Integer, List<Integer>> graph = new HashMap<>();
    static int[] dist = new int[N];
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        for (int i = 1; i <= n; i++) {
            graph.put(i, new ArrayList<>());
        }
        m = in.nextInt();
        for (int i = 0; i < m; i++) {
            int a = in.nextInt();
            int b = in.nextInt();
            graph.get(a).add(b);
        }
        System.out.println(bfs());
    }
    private static int bfs(){
        Arrays.fill(dist, -1);
        Queue<Integer> queue = new LinkedList<>();
        queue.offer(1);
        dist[1] = 0;
        while(!queue.isEmpty()){
            int i = queue.poll();
            for(int j : graph.get(i)){
                if(dist[j] == -1){
                    dist[j] = dist[i] + 1;
                    queue.offer(j);
                }
            }
        }
        return dist[n];
    }
}

拓扑排序

848. 有向图的拓扑序列

import java.util.*;

public class Main {

    static int N = 100010;

    static int n, m;
    static Map<Integer, List<Integer>> graph = new HashMap<>();
    static List<Integer> res = new ArrayList<>();
    static int[] inD = new int[N];

    static void add(int a, int b) {
        graph.get(a).add(b);
    }
    static boolean topsort() {
        Queue<Integer> queue = new LinkedList<>();
        for(int i = 1; i <= n; i++){
            if(inD[i] == 0){
                queue.offer(i);
            }
        }
        int count = 0;
        while(!queue.isEmpty()){
            int node = queue.poll();
            res.add(node);
            count++;	
            List<Integer> edges = graph.get(node);
            if(edges == null) continue;
            for(Integer neibor : edges){
                if(--inD[neibor] == 0){
                    queue.offer(neibor);
                }
            }
        }
        return count == n;
    }
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        m = in.nextInt();
        for(int i = 1; i <= n; i++){
            graph.put(i, new ArrayList<>());
        }
        for (int i = 0; i < m; i++) {
            int a = in.nextInt(), b = in.nextInt();
            add(a, b);
            inD[b]++;
        }
        if (!topsort()) System.out.println("-1");
        else {
            for(int i = 0; i < n; i++){
                System.out.print(res.get(i) + " ");
            }
        }
    }
}
//第二种:得到多少层次数量
import java.util.*;
public class Main {
    static int N = 100010;
    static int[] dist = new int[N];
    static Map<Integer, List<Integer>> graph = new HashMap<>();
    static int[] inD = new int[N];
    static int[] levels = new int[N];
    static int res;
    static int n;
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        for(int i = 0; i < N; i++){
            graph.put(i, new ArrayList<>());
        }
        n = in.nextInt();
        for(int i = 1; i <= n; i++){
            int m = in.nextInt();
            for(int j = 0; j < m; j++){
                int a = i;
                int b = in.nextInt();
                graph.get(b).add(a);
                inD[a]++;
            }
        }
        if(!topSort()){
            System.out.println(-1);
            return;
        }
        System.out.println(res);
    }
    private static boolean topSort(){
        int count = 0;
        int level = 0;
        Queue<Integer> queue = new LinkedList<>();
        for(int i = 1; i <= n; i++){
            if(inD[i] == 0){
                queue.offer(i);
                levels[i] = 1;
            }
        }
        while(!queue.isEmpty()){
            int size = queue.size();
            level++;
            for(int i = 0; i < size; i++){//层次遍历
                int j = queue.poll();
                count++;
                List<Integer> edges = graph.get(j);
                if(edges.size() == 0) continue;
                for(int k : edges){
                    if(--inD[k] == 0){
                        queue.add(k);
                        levels[k] = level + 1;
                    }
                }
            }
        }
        res = level;
        return count == n;
    }
}

DP

01背包----每件物品只能使用一次。

//状态表示:dp[i][j]:只考虑前i个物品,且总体积不大于j的所有选法,【dp:最大价值】
//状态计算:dp(i,j) = max(dp(i-1,j),dp(i-1,j-v[i])+w[i])第i件物品时,拿0个或者拿1个,取max
import java.util.*;
public class Main {
    static int N = 1010;
    static int[][] dp = new int[N][N];
    static int[] w = new int[N];
    static int[] v = new int[N];
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        for(int i = 1; i <= n; i++){
            v[i] = in.nextInt();
            w[i] = in.nextInt();
        }
        //dp[0][0~m] = 0;
        for(int i = 1; i <= n; i++){
            for(int j = 0; j <= m; j++){
                dp[i][j] = dp[i-1][j];//拿0个
                if(j-v[i] >= 0){//拿1个
                    dp[i][j] = Math.max(dp[i][j], dp[i-1][j-v[i]]+w[i]);
                }
            }
        }
        System.out.println(dp[n][m]);
    }
}
//滚动数组优化从大到小,因为dp[j-v[i]]在dp[j]之前就已经被计算过了。
//如果j++:dp[j-v[i]]==dp[i][j-v[i]],j--:dp[j-v[i]]==dp[i-1][j-v[i]]
import java.util.*;
public class Main {
    static int N = 1010;
    static int[] dp = new int[N];
    static int[] w = new int[N];
    static int[] v = new int[N];
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        for(int i = 1; i <= n; i++){
            v[i] = in.nextInt();
            w[i] = in.nextInt();
        }
        for(int i = 1; i <= n; i++){
            for(int j = m; j >= v[i]; j--){
                dp[j] = Math.max(dp[j], dp[j-v[i]] + w[i]);
            }
        }
        System.out.println(dp[m]);
    }
}
//dp[i][j]:dp[i-1][j]+dp[i-1][j-v[i]]表示在前i个物品中选取若干个物品,恰好凑成j重量的方案数。					                                              --【dp:求选择方案数】
import java.util.*;

public class Main {
    static int N = 1010;
    static int[][] dp = new int[N][N];
    static int[] v = new int[N];
    static int[] w = new int[N];
    
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        for(int i = 1; i <= n; i++) {
            v[i] = in.nextInt();
            w[i] = in.nextInt();
        }
        dp[0][0] = 1;
        for(int i = 1; i <= n; i++) {
            for(int j = 0; j <= m; j++) {
                dp[i][j] = dp[i-1][j];
                if(j - v[i] >= 0) {
                    dp[i][j] = dp[i][j] + dp[i-1][j-v[i]];
                }
            }
        }
        System.out.println(dp[n][m]);
    }
}

完全背包----每种物品都有无限件可用。

//状态表示:dp[i][j]:所有只考虑前i个物品,且总体积不大于j的所有选法 【dp:最大价值】
//状态计算:dp[i][j] = dp[i-1,j-v[i]*k] + w[i]*k:第i个物品,拿0个,拿1个,...拿k个,取max
import java.util.*;
public class Main {
    static int N = 1010;
    static int[][] dp = new int[N][N];
    static int[] v = new int[N];
    static int[] w = new int[N];
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        for(int i = 1; i <= n; i++){
            v[i] = in.nextInt();
            w[i] = in.nextInt();
        }
        for(int i = 1; i <= n; i++){
            for(int j = 0; j <= m; j++){
                for(int k = 0; k * v[i] <= j; k++){
                    dp[i][j] = Math.max(dp[i][j], dp[i-1][j-k*v[i]] + k*w[i]);
                }
            }
        }
        System.out.println(dp[n][m]);
    }
}
//优化为二维:
import java.util.*;
public class Main {
    static int N = 1010;
    static int[][] dp = new int[N][N];
    static int[] v = new int[N];
    static int[] w = new int[N];
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        for(int i = 1; i <= n; i++){
            v[i] = in.nextInt();
            w[i] = in.nextInt();
        }
        for(int i = 1; i <= n; i++){
            for(int j = 0; j <= m; j++){
                dp[i][j] = dp[i-1][j];
                if(j-v[i] >= 0){
                    dp[i][j] = Math.max(dp[i][j], dp[i][j-v[i]]+w[i]);
                }
            }
        }
        System.out.println(dp[n][m]);
    }
}
//滚动数组优化一维:
 import java.util.*;
public class Main {
    static int N = 1010;
    static int[] dp = new int[N];
    static int[] v = new int[N];
    static int[] w = new int[N];
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        for(int i = 1; i <= n; i++){
            v[i] = in.nextInt();
            w[i] = in.nextInt();
        }
        for(int i = 1; i <= n; i++){
            for(int j = v[i]; j <= m; j++){
                dp[j] = Math.max(dp[j], dp[j-v[i]] + w[i]);
            }
        }
        System.out.println(dp[m]);
    }
}

多重背包 I

//状态表示:dp[i][j]:所有只考虑前i个物品,且总体积不大于j的所有选法 【dp:最大价值】
//状态计算:dp[i][j] = dp[i-1,j-v[i]*k] + w[i]*k; k=0,1,2,...,s[i]; 取max
import java.util.*;
public class Main {
    static int N = 110;
    static int[][] dp = new int[N][N];
    static int[] v = new int[N];
    static int[] w = new int[N];
    static int[] s = new int[N];
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        for(int i = 1; i <= n; i++){
            v[i] = in.nextInt();
            w[i] = in.nextInt();
            s[i] = in.nextInt();
        }
        for(int i = 1; i <= n; i++){
            for(int j = 0; j <= m; j++){
                for(int k = 0; k <= s[i] && k*v[i] <= j; k++){
                    dp[i][j] = Math.max(dp[i][j], dp[i-1][j-k*v[i]] + k*w[i]);
                }
            }
        }
        System.out.println(dp[n][m]);
    }
}

多重背包问题 II

//二进制优化:
import java.util.*;
public class Main {
    static int N = 2010;
    static int M = 12000;//2000是体积,12000是分解后的物品的个数
    static int dp[] = new int[N];
    static int v[] = new int[M];
    static int w[] = new int[M];
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        int cnt = 0;
        for(int i = 1; i <= n; i++){
            int a = in.nextInt();
            int b = in.nextInt();
            int s = in.nextInt();
            //二进制优化
            int k = 1;
            while(k <= s){
                cnt++;
                v[cnt] = a * k;
                w[cnt] = b * k;
                s -= k;
                k *= 2;
            }
            if(s > 0){
                cnt++;
                v[cnt] = a * s;
                w[cnt] = b * s;
            }
        }
        n = cnt;
        for(int i = 1; i <= n; i++){
            for(int j = m; j >= v[i]; j--){
                dp[j] = Math.max(dp[j], dp[j-v[i]] + w[i]);
            }
        }
        System.out.println(dp[m]);
    }
}

分组背包

//状态表示:dp[i][j]:所有只考虑前i个物品,且总体积不大于j的所有选法 【dp:最大价值】
//状态计算:dp[i][j] = dp[i-1,j-v[i]*k] + w[i]*k; k=0,1,2,...,s[i]; 取max
import java.util.*;
public class Main {
    static int N = 110;
    static int[][] dp = new int[N][N];
    static int[][] v = new int[N][N];
    static int[][] w = new int[N][N];
    static int[] s = new int[N];
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        for(int i = 1; i <= n; i++){
            s[i] = in.nextInt();
            for(int j = 1; j <= s[i]; j++){
                v[i][j] = in.nextInt();
                w[i][j] = in.nextInt();
            }
        }
        for(int i = 1; i <= n; i++){
            for(int j = 0; j <= m; j++){
                dp[i][j] = dp[i-1][j];
                for(int k = 1; k <= s[i]; k++){
                    if(v[i][k] <= j){
                        dp[i][j] = Math.max(dp[i][j], dp[i-1][j-v[i][k]] + w[i][k]);
                    }
                }
            }
        }
        System.out.println(dp[n][m]);
    }
}
//优化一维度:
import java.util.Scanner;

public class Main {
    static int N = 110;
    static int[][] v = new int[N][N];
    static int[][] w = new int[N][N];
    static int[] s = new int[N];//表示每一组的个数
    static int[] f = new int[N];
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        int m = scan.nextInt();
        for(int i = 1;i <= n;i++)
        {
            s[i] = scan.nextInt();
            for(int j = 1;j <= s[i];j++)
            {
                v[i][j] = scan.nextInt();
                w[i][j] = scan.nextInt();
            }
        }
        //一个都不选的方案在状态优化成1维的时候就可以省略了,因为本层的f[j]就是上一层的f[j]。
        for(int i = 1;i <= n;i++)
            for(int j = m;j >= 0;j--)
                for(int k = 1;k <= s[i];k++)//选第k个,k=0时v[i][k]为0
                    if(v[i][k] <= j)
                        f[j] = Math.max(f[j], f[j - v[i][k]] + w[i][k]);

        System.out.println(f[m]);

    }
}

线性DP

数字三角形

import java.util.*;
public class Main {
    static int N = 510;
    static int[][] arr = new int[N][N];
    static int[][] dp = new int[N][N];
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        for(int i = 1; i <= n; i ++){
            for(int j = 1; j <= i; j ++){
                arr[i][j] = in.nextInt();
            }
        }

        for(int i = 0; i <= n; i ++){
            for(int j = 0; j <= i + 1; j ++){
                dp[i][j] = Integer.MIN_VALUE;
            }
        }

        dp[1][1] = arr[1][1];
        for(int i = 2; i <= n; i ++){
            for(int j = 1; j <= i; j ++){
                dp[i][j] = Math.max(dp[i - 1][j - 1], dp[i - 1][j]) + arr[i][j];//dp[i-1][j-1] + arr[i][j]Integer.MIN_VALUE越界
            }
        }
        

        int res = Integer.MIN_VALUE;
        for(int i = 1; i <= n; i++){
            res = Math.max(res, dp[n][i]);
        }
        System.out.println(res);
    }
}

最长上升子序列

//记录路径path
import java.lang.reflect.Array;
import java.util.*;
public class Main {
    static int N = 1010;
    static int[] nums = new int[N];
    static int[] dp = new int[N];
    static int[] pre = new int[N];
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        for(int i = 1; i <= n; i++){
            nums[i] = in.nextInt();
        }
        Arrays.fill(dp, 1);
        for(int i = 1; i <= n; i++){
            pre[i] = 0;
            for(int j = 1; j < i; j++){
                if(nums[j] < nums[i]){
                    if(dp[i] < dp[j] + 1){
                        dp[i] = dp[j] + 1;
                        pre[i] = j;
                    }
                }
            }
        }
        int res = dp[1];
        int k = 0;
        for(int i = 2; i <= n; i++){
            if(dp[i] > res){
                res = dp[i];
                k = i;
            }
        }
        System.out.println(res);
        int temp = dp[k];
        for(int i = 0; i < temp; i++){
            System.out.print(nums[k] + " ");
            k = pre[k];
        }
    }

}
import java.util.*;
public class Main {
    static int N = 1010;
    static int[] nums = new int[N];
    static int[] dp = new int[N];
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        for(int i = 1; i <= n; i++){
            nums[i] = in.nextInt();
        }
        Arrays.fill(dp, 1);
        for(int i = 1; i <= n; i++){
            for(int j = 1; j < i; j++){
                if(nums[j] < nums[i]){
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
        }
        int res = dp[1];
        for(int i = 2; i <= n; i++){
            if(dp[i] > res){
                res = dp[i];
            }
        }
        System.out.println(res);
    }
}

最长上升子序列 II


最短编辑距离


编辑距离


贪心

905. 区间选点

/*
1.将每个区间按右端点从小到大排序
2.从前往后依次枚举每个区间
	如果当前区间中已经包含点,则直接pass
	否则,选择当前区间右端点
*/
import javax.swing.text.Segment;
import java.util.Arrays;
import java.util.Scanner;

class Range {
    int l, r;
    public Range(int l, int r){
        this.l = l;
        this.r = r;
    }
}

public class Main {
    static int N = 100010;
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        Range[] ranges= new Range[n];
        for(int i = 0; i < n; i++){
            int l = in.nextInt();
            int r = in.nextInt();
            ranges[i] = new Range(l, r);
        }
        Arrays.sort(ranges, (o1, o2) -> o1.r - o2.r);
        int res = 0;
        int end = Integer.MIN_VALUE;//上一个点的下标
        for(int i = 0; i < n; i++){
            Range cur = ranges[i];
            if(end < cur.l){
                res++;
                end = cur.r;
            }
        }
        System.out.println(res);
    }
}

906. 区间分组

/*
1.将每个区间按左端点从小到大排序
2.从前往后依次枚举每个区间
	判断是否能放入现在的组内:L[i] > Max_r
		1. 如果不存则开新组
		2. 如果存在,放入该组,并更新当前组Max_r
*/
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Scanner;

class Range {
    int l, r;
    public Range(int l, int r){
        this.l = l;
        this.r = r;
    }
}

public class Main {
    static int N = 100010;
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        Range[] ranges= new Range[n];
        for(int i = 0; i < n; i++){
            int l = in.nextInt();
            int r = in.nextInt();
            ranges[i] = new Range(l, r);
        }
        Arrays.sort(ranges, (o1, o2) -> o1.l - o2.l);
        Queue<Integer> minheap = new PriorityQueue<>(); // 小根堆
        for(Range cur : ranges){
            if(minheap.isEmpty() || minheap.peek() >= cur.l){//如果当前组的最右端大于等于当前区间的左端,说明当前区间和当前组有重合,则新建一个分组
                minheap.add(cur.r);
            }else{//如果当前组的最右端小于当前区间的左端,说明该区间可以直接添加到当前分组,所以要更新当前分组的信息
                minheap.poll();
                minheap.add(cur.r);
            }
        }
        System.out.println(minheap.size());
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Guanam_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值