单链表
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]] + " ");
}
}
}
}
并查集
- 将两个集合合并
- 询问两个元素是否在一个集合当中
每个集合用一颗树表示。树根的编号就是整个集合的编号。每个节点存储它的父节点,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];
}
}
堆
- 插入一个数:heap[++size] = x;up(size);
- 求集合当中的最小值:heap[1];
- 删除最小值:heap[1] = heap[size]; size–;down(1)
- 删除任意一个元素:heap[k] = heap[size]; size–;down(k) / up(k)
- 修改任意一个元素: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);
}
}
贪心
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());
}
}