diff --git a/ACWing/src/RMQ/ST.java b/ACWing/src/RMQ/ST.java index 748a9ad49e42a973d8c039cf9fd60b6c4169ab4c..fc5b2d7d78cb6c7dc39bee3ddc1090c734f304fe 100644 --- a/ACWing/src/RMQ/ST.java +++ b/ACWing/src/RMQ/ST.java @@ -36,7 +36,6 @@ public class ST { } } - } static int[] ran(int a) { diff --git a/ACWing/src/graph/Spfa.java b/ACWing/src/graph/Spfa.java new file mode 100644 index 0000000000000000000000000000000000000000..352b31deec147d4aeaffd876498558b5ba853b05 --- /dev/null +++ b/ACWing/src/graph/Spfa.java @@ -0,0 +1,90 @@ +package graph; + +import java.util.ArrayDeque; +import java.util.Arrays; +import java.util.Scanner; + +/** + * 给定一个n个点m条边的有向图,图中可能存在重边和自环, 边权可能为负数。 + * 请你求出1号点到n号点的最短距离,如果无法从1号点走到n号点,则输出impossible。 + * 数据保证不存在负权回路。 + * 输入格式 + * 第一行包含整数n和m。 + * 接下来m行每行包含三个整数x,y,z,表示点x和点y之间存在一条有向边,边长为z。 + * 输出格式 + * 输出一个整数,表示1号点到n号点的最短距离。 + * 如果路径不存在,则输出”impossible”。 + * 数据范围 + * 1≤n,m≤10^5, + * 图中涉及边长绝对值均不超过10000。 + * 输入样例: + * 3 3 + * 1 2 5 + * 2 3 -3 + * 1 3 4 + *

+ * 输出样例: + * 2 + * Bellman-Ford算法的时间复杂度为O(mn),处理本题显然会超时, + * 而spfa算法则是采用了队列优化后的Bellman-Ford算法, + * spfa算法一般情况下的时间复杂度为O(m),最坏情况下的时间复杂度为O(mn)。 + * 要理解spfa算法的思路很简单,只需要理解Bellman-Ford算法的缺点即可。 + * 回忆下Bellman-Ford算法,每轮松弛都松弛所有的边, + * 在执行过程中可以发现每轮只有很少的边会被松弛成功,因为假设第i轮仅松弛了a节点的距离, + * 那么在下一轮的松弛中,除了a可以到达的点有被松弛的可能性,其它的点不用判断也能知道不会被松弛。 + * 既然事先已经知道有些边不会被松弛,那么为什么还要继续去遍历所有边呢?spfa算法的核心思想就是: + * 每一轮只遍历上一轮被松弛的点连接的边,只松弛也可能被松弛的边。 + */ +public class Spfa { + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + n = sc.nextInt(); + m = sc.nextInt(); + int x, y, z; + for (int i = 0; i < m; i++) { + x = sc.nextInt(); + y = sc.nextInt(); + z = sc.nextInt(); + add(x, y, z); + } + Arrays.fill(dis, Integer.MAX_VALUE); + dis[1] = 0; + vis[1] = true; + ArrayDeque q = new ArrayDeque(); + q.add(1); + int t = 0; + while (!q.isEmpty()) { + x = q.poll(); + vis[x] = false; + for (int i = he[x]; i != 0; i = ne[i]) { + t = e[i]; + if (dis[x] != Integer.MAX_VALUE && dis[t] > dis[x] + w[i]) { + dis[t] = dis[x] + w[i]; + if (!vis[t]) { + if (!q.isEmpty()&&dis[t] { + int to, dis; + + public node(int to, int dis) { + this.to = to; + this.dis = dis; + } + + @Override + public int compareTo(node node) { + return dis - node.dis; + } + + } + + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + n = sc.nextInt(); + m = sc.nextInt(); + int a, b, c; + for (int i = 0; i < m; i++) { + a = sc.nextInt(); + b = sc.nextInt(); + c = sc.nextInt(); + add(a, b, c); + } + Arrays.fill(dis, Integer.MAX_VALUE); + dij(); + } + + private static void dij() { + q.add(new node(1, 0)); + dis[1] = 0; + while (!q.isEmpty()) { + node p = q.poll(); + if (vis[p.to]) continue; + vis[p.to] = true; + for (int i = he[p.to]; i != 0; i = ne[i]) { + int ed = e[i]; + if (dis[p.to] != Integer.MAX_VALUE && dis[ed] > p.dis + w[i]) { + dis[ed] = p.dis + w[i]; + q.add(new node(ed, dis[ed])); + } + } + } + if (dis[n] != Integer.MAX_VALUE) + System.out.println(dis[n]); + else System.out.println("No"); + } + + static void add(int a, int b, int c) { + e[cnt] = b; + w[cnt] = c; + ne[cnt] = he[a]; + he[a] = cnt++; + } + + static int n, m, cnt = 1; + static PriorityQueue q = new PriorityQueue(); + static int[] he = new int[100005]; + static int[] dis = new int[100005]; + static int[] ne = new int[100005]; + static int[] w = new int[100005]; + + static int[] e = new int[100005]; + static boolean[] vis = new boolean[100005]; + +} diff --git a/ACWing/src/graph/floyd.java b/ACWing/src/graph/floyd.java new file mode 100644 index 0000000000000000000000000000000000000000..96870585a432a929d00efe07ff04b510f4c50bea --- /dev/null +++ b/ACWing/src/graph/floyd.java @@ -0,0 +1,7 @@ +package graph; + +public class floyd { + public static void main(String[] args) { + + } +} diff --git a/ACWing/src/graph/kruskal.java b/ACWing/src/graph/kruskal.java new file mode 100644 index 0000000000000000000000000000000000000000..0da506baac45b6f6c72e8653d356d8d38ec2facb --- /dev/null +++ b/ACWing/src/graph/kruskal.java @@ -0,0 +1,111 @@ +package graph; + +import java.util.PriorityQueue; +import java.util.Scanner; + +/** + * 给定一个n个点m条边的无向图,图中可能存在重边和自环,边权可能为负数。 + * 求最小生成树的树边权重之和,如果最小生成树不存在则输出impossible。 + * 给定一张边带权的无向图G=(V, E),其中V表示图中点的集合,E表示图中边的集合,n=|V|,m=|E|。 + * 由V中的全部n个顶点和E中n-1条边构成的无向连通子图被称为G的一棵生成树, + * 其中边的权值之和最小的生成树被称为无向图G的最小生成树。 + * 输入格式 + * 第一行包含两个整数n和m。 + * 接下来m行,每行包含三个整数u,v,w,表示点u和点v之间存在一条权值为w的边。 + * 输出格式 + * 共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和, + * 如果最小生成树不存在则输出impossible。 + * 数据范围 + * 1≤n≤10^5, + * 1≤m≤2∗10^5, + * 图中涉及边的边权的绝对值均不超过1000。 + * 输入样例: + * 4 5 + * 1 2 1 + * 1 3 2 + * 1 4 3 + * 2 3 2 + * 3 4 4 + * 输出样例: + * 6 + * 分析: + * kruskal算法的时间复杂度为O(mlogm),用于求解稀疏图的最小生成树问题, + * 而prim算法用于解决稠密图的最小生成树问题。kruskal算法是基于贪心思想的, + * 每次选取权重最小的边加入最小生成树,并且需要保证新加入的边不会构成环。 + * kruskal算法一般使用并查集实现,具体算法的流程就是先对各个边按边权从小到大排序, + * 按照边权从小到大选取边加入最小生成树的集合,判断新加入的边是否会构成环, + * 只需要判断下边的两个顶点是否在同一集合中即可, + * 在则说明边的两个顶点均已加入并查集中,不可再添加边了。 + */ +public class kruskal { + public static void main(String[] args) { + PriorityQueue q = new PriorityQueue(); + Scanner sc = new Scanner(System.in); + n = sc.nextInt(); + m = sc.nextInt(); + int a, b, c; + for (int i = 0; i < m; i++) { + a = sc.nextInt(); + b = sc.nextInt(); + c = sc.nextInt(); + q.add(new node(a, b, c)); + } + int res = 0; + int cnt = 0; + while (!q.isEmpty()) { + node p = q.poll(); + if (!is(p.x, p.y)) { + res += p.w; + union(p.x, p.y); + cnt++; + } + } + if (cnt==n-1) + System.out.println(res); + else System.out.println("No"); + } + + static boolean is(int p, int q) { + return find(p) == find(q); + } + + static class node implements Comparable { + int x, y, w; + + public node(int x, int y, int w) { + this.x = x; + this.y = y; + this.w = w; + } + + @Override + public int compareTo(node node) { + return w - node.w; + } + } + + static int n, m; + + static int[] par = new int[200005]; + + static { + for (int i = 0; i < par.length; i++) { + par[i] = i; + } + } + + static void union(int a, int b) { + int aroot = find(a); + int broot = find(b); + if (aroot != broot) + par[aroot] = broot; + } + + static int find(int x) { + while (x != par[x]) { + par[x] = par[par[x]]; + x = par[x]; + } + return x; + } +} diff --git a/ACWing/src/graph/prim.java b/ACWing/src/graph/prim.java new file mode 100644 index 0000000000000000000000000000000000000000..1af61884bae9eefcf99d84b9b2248bebc8f14049 --- /dev/null +++ b/ACWing/src/graph/prim.java @@ -0,0 +1,113 @@ +package graph; + +import java.util.Arrays; +import java.util.PriorityQueue; +import java.util.Scanner; + +/** + * 给定一个n个点m条边的无向图,图中可能存在重边和自环,边权可能为负数。 + * 求最小生成树的树边权重之和,如果最小生成树不存在则输出impossible。 + * 给定一张边带权的无向图G=(V, E),其中V表示图中点的集合,E表示图中边的集合,n=|V|,m=|E|。 + * 由V中的全部n个顶点和E中n-1条边构成的无向连通子图被称为G的一棵生成树, + * 其中边的权值之和最小的生成树被称为无向图G的最小生成树。 + * 输入格式 + * 第一行包含两个整数n和m。 + * 接下来m行,每行包含三个整数u,v,w,表示点u和点v之间存在一条权值为w的边。 + * 输出格式 + * 共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出impossible。 + * 数据范围 + * 1≤n≤500, + * 1≤m≤10^5, + * 图中涉及边的边权的绝对值均不超过10000。 + * 输入样例: + * 4 5 + * 1 2 1 + * 1 3 2 + * 1 4 3 + * 2 3 2 + * 3 4 4 + * 输出样例: + * 6 + * 分析: + * prim算法与dijkstra算法十分类似。都是基于点集的, + * 每次找离点集最近的点,连上点集到该点的边。 + * 不同的是dijkstra算法用松弛操作来更新最短路径, + * 而prim算法只需要每次选离点集最近的点,将边长加入最小生成树的权重中即可。 + * 如何寻找离点集最近的点,只需修改下dijkstra算法d数组的含义为某点向点集中连接的边的最小边的长度即可。 + * 每次加入某条边进生成树,即用选中的边的顶点更新其他连接边的最短边的长度 + */ +public class prim { + static class node implements Comparable { + int x, y, z; + + public node(int x, int y, int w) { + this.x = x; + this.y = y; + this.z = w; + } + + @Override + public int compareTo(node node) { + return z - node.z; + } + + @Override + public String toString() { + return "node{" + + "x=" + x + + ", y=" + y + + ", z=" + z + + '}'; + } + } + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + n = sc.nextInt(); + m = sc.nextInt(); + int a, b, c; + for (int i = 0; i < g.length; i++) { + Arrays.fill(g[i], Integer.MAX_VALUE); + } + for (int i = 0; i < m; i++) { + a = sc.nextInt(); + b = sc.nextInt(); + c = sc.nextInt(); + g[a][b] = g[b][a] = Math.min(g[a][n], c);//处理自环边 + } + int res = prime(); + if (res == -1) System.out.println("NO"); + else System.out.println(res); + } + + private static int prime() { + vis[1] = true; + int res = 0; + for (int i = 1; i <= n; i++) { + if (g[1][i] != Integer.MAX_VALUE) q.add(new node(1, i, g[1][i])); + } + int cnt = 0; + while (!q.isEmpty()) { + node min = q.poll(); + if (vis[min.x] && vis[min.y]) continue; + res += min.z; + cnt++; + int newV = vis[min.x] ? min.y : min.x; + vis[newV] = true; + for (int i = 1; i <= n; i++) { + if (g[newV][i] != Integer.MAX_VALUE && !vis[i]) { + q.add(new node(newV, i, g[newV][i])); + } + } + } + if (cnt != n - 1) return -1; + return res; + } + + static PriorityQueue q = new PriorityQueue(); + + static boolean[] vis = new boolean[510]; + static int n, m; + static int[][] g = new int[510][510]; + +} diff --git "a/ACWing/src/graph/spfa\345\210\244\346\226\255\350\264\237\347\216\257.java" "b/ACWing/src/graph/spfa\345\210\244\346\226\255\350\264\237\347\216\257.java" new file mode 100644 index 0000000000000000000000000000000000000000..c60a9cc42a5df61bcf53e089afd830485fdbc6d8 --- /dev/null +++ "b/ACWing/src/graph/spfa\345\210\244\346\226\255\350\264\237\347\216\257.java" @@ -0,0 +1,96 @@ +package graph; + +import java.util.ArrayDeque; +import java.util.Scanner; + +/** + * 给定一个n个点m条边的有向图,图中可能存在重边和自环, 边权可能为负数。 + * 请你判断图中是否存在负权回路。 + * 输入格式 + * 第一行包含整数n和m。 + * 接下来m行每行包含三个整数x,y,z,表示点x和点y之间存在一条有向边,边长为z。 + * 输出格式 + * 如果图中存在负权回路,则输出“Yes”,否则输出“No”。 + * 数据范围 + * 1≤n≤2000, + * 1≤m≤10000, + * 图中涉及边长绝对值均不超过10000。 + * 输入样例: + * 3 3 + * 1 2 -1 + * 2 3 4 + * 3 1 -4 + * 输出样例: + * Yes + * 分析: + * 在Bellman-Ford算法中,我们知道,经过最大n-1轮松弛操作, + * 从起点到所有点的最短距离就被确定下了。而图中若存在负权回路, + * 则每次通过该负权回路来松弛距离时,最短距离都会减小, + * 是不存在最短路径的。所以可以在spfa算法中用cnt数组存储当前点最短路径经过的边数, + * 一旦经过了n条边,则说明经过了n + 1个点, + * 而图中仅有n个点,该最短路径经过了一个点两次,所以必然存在负权回路。 + */ +public class spfa判断负环 { + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + n = sc.nextInt(); + m = sc.nextInt(); + int x, y, z; + for (int i = 0; i < m; i++) { + x = sc.nextInt(); + y = sc.nextInt(); + z = sc.nextInt(); + add(x, y, z); + } + if (spfa()) System.out.println("YES"); + else System.out.println("NO"); + } + + static boolean spfa() { + // 不需要初始化dist数组 + // 原理:如果某条最短路径上有n个点(除了自己),那么加上自己之后一共有n+1个点,由抽屉原理一定有两个点相同,所以存在环。 + ArrayDeque q = new ArrayDeque(100000); + for (int i = 1; i <= n; i++) { + q.add(i); + vis[i] = true; + } + int t = 0,x; + while (!q.isEmpty()) { + x = q.poll(); + vis[x] = false; + for (int i = he[x]; i != 0; i = ne[i]) { + t = e[i]; + if (dis[x] != Integer.MAX_VALUE && dis[t] > dis[x] + w[i]) { + dis[t] = dis[x] + w[i]; + count[t] = count[x] + 1; + if (count[t] >= n) return true; + if (!vis[t]) { + if (!q.isEmpty() && dis[t] < dis[q.peekFirst()]) { + q.addFirst(t); + } else q.add(t); + vis[t] = true; + } + } + } + } + return false; + } + + static int[] count = new int[100005]; + static int[] dis = new int[100005]; + static boolean[] vis = new boolean[100005]; + static int[] he = new int[100005]; + static int[] w = new int[200005]; + static int[] ne = new int[200005]; + static int[] e = new int[200005]; + + static void add(int a, int b, int c) { + e[cnt] = b; + w[cnt] = c; + ne[cnt] = he[a]; + he[a] = cnt++; + } + + static int n, m, cnt = 1; +} diff --git "a/ACWing/src/graph/\345\214\210\347\211\231\345\210\251\347\256\227\346\263\225.java" "b/ACWing/src/graph/\345\214\210\347\211\231\345\210\251\347\256\227\346\263\225.java" new file mode 100644 index 0000000000000000000000000000000000000000..6c5a14c0b51da8e5368ef04178d7423cf2d6f7c7 --- /dev/null +++ "b/ACWing/src/graph/\345\214\210\347\211\231\345\210\251\347\256\227\346\263\225.java" @@ -0,0 +1,4 @@ +package graph; +//二分图 +public class 匈牙利算法 { +} diff --git "a/ACWing/src/graph/\346\213\223\346\211\221\346\216\222\345\272\217.java" "b/ACWing/src/graph/\346\213\223\346\211\221\346\216\222\345\272\217.java" new file mode 100644 index 0000000000000000000000000000000000000000..c0cc58ba1698b8bbde06a3c6cf18cbc04528a1ab --- /dev/null +++ "b/ACWing/src/graph/\346\213\223\346\211\221\346\216\222\345\272\217.java" @@ -0,0 +1,73 @@ +package graph; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Scanner; + +/** + * 题目描述: + * 给定一个n个点m条边的有向图,图中可能存在重边和自环。 + * 请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出-1。 + * 若一个由图中所有点构成的序列A满足:对于图中的每条边(x, y),x在A中都出现在y之前,则称A是该图的一个拓扑序列。 + * 输入格式 + * 第一行包含两个整数n和m + * 接下来m行,每行包含两个整数x和y,表示点x和点y之间存在一条有向边(x, y)。 + * 输出格式 + * 共一行,如果存在拓扑序列,则输出拓扑序列。 + * 否则输出-1。 + * 数据范围 + * 1≤n,m≤10^5 + * 输入样例: + * 3 3 + * 1 2 + * 2 3 + * 1 3 + * 输出样例: + * 1 2 3 + */ +public class 拓扑排序 { + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + n = sc.nextInt(); + m = sc.nextInt(); + int a, b; + for (int i = 0; i < m; i++) { + a = sc.nextInt(); + b = sc.nextInt(); + add(a, b); + inDegree[b]++; + } + ArrayDeque q = new ArrayDeque(); + for (int i = 1; i <= n; i++) { + if (inDegree[i] == 0) + q.add(i); + } + ArrayList res = new ArrayList(); + while (!q.isEmpty()) { + a = q.poll(); + res.add(a); + for (int i = he[a]; i != 0; i = ne[i]) { + b = e[i]; + inDegree[b]--; + if (inDegree[b] == 0) q.add(b); + } + } + if (res.size()==n){ + System.out.println(res); + }else System.out.println("No"); + } + + private static void add(int a, int b) { + e[cnt] = b; + ne[cnt] = he[a]; + he[a] = cnt++; + } + + static int cnt = 1; + static int[] he = new int[100005]; + static int[] ne = new int[100005]; + static int[] e = new int[100005]; + static int[] inDegree = new int[100005]; + + static int n, m; +} diff --git "a/ACWing/src/graph/\346\237\223\350\211\262\345\210\244\346\226\255\344\272\214\345\210\206\345\233\276.java" "b/ACWing/src/graph/\346\237\223\350\211\262\345\210\244\346\226\255\344\272\214\345\210\206\345\233\276.java" new file mode 100644 index 0000000000000000000000000000000000000000..13648ffe44ed5049c54f0f821818316fb46ef4ea --- /dev/null +++ "b/ACWing/src/graph/\346\237\223\350\211\262\345\210\244\346\226\255\344\272\214\345\210\206\345\233\276.java" @@ -0,0 +1,70 @@ +package graph; + +import java.util.Arrays; +import java.util.Scanner; + +/** + * 给定一个n个点m条边的无向图,图中可能存在重边和自环。请你判断这个图是否是二分图。 + * 输入格式 + * 第一行包含两个整数n和m。 + * 接下来m行,每行包含两个整数u和v,表示点u和点v之间存在一条边。 + * 如果给定图是二分图,则输出“Yes”,否则输出“No”。 + * 数据范围 + * 1≤n,m≤10^5 + * 输入样例: + * 4 4 + * 1 3 + * 1 4 + * 2 3 + * 2 4 + * 输出样例: + * Yes + * 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B), + * 并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B), + * 则称图G为一个二分图。简单的说,就是让图中任意一条边上的两个顶点都属于不同的集合即可,比如下图: + */ +public class 染色判断二分图 { + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + n = sc.nextInt(); + m = sc.nextInt(); + int a, b; + for (int i = 0; i < m; i++) { + a = sc.nextInt(); + b = sc.nextInt(); + add(a, b); + add(b, a); + } + Arrays.fill(vis, -1); + for (int i = 1; i <= n; i++) { + if (vis[i] == -1) + if (dfs(1, 0)) System.out.println("YES"); + else System.out.println("NO"); + } + System.out.println(Arrays.toString(vis)); + } + + static boolean dfs(int u, int id) { + vis[u] = id; + for (int i = he[u]; i != 0; i = ne[i]) { + int ed = e[i]; + if (vis[ed] == -1) { + if (!dfs(ed, 1 - id)) return true; + } else if (vis[ed] == vis[u]) return false; + } + return true; + } + + static int[] vis = new int[100005]; + static int[] he = new int[100005]; + static int[] ne = new int[200005];//无向图开2倍 + static int[] e = new int[200005]; + + static void add(int a, int b) { + e[cnt] = b; + ne[cnt] = he[a]; + he[a] = cnt++; + } + + static int n, m, cnt = 1; +} diff --git "a/ACWing/src/\346\240\221\345\275\242dp/\346\234\211\344\276\235\350\265\226\347\232\204\350\203\214\345\214\205\351\227\256\351\242\230.java" "b/ACWing/src/\346\240\221\345\275\242dp/\346\234\211\344\276\235\350\265\226\347\232\204\350\203\214\345\214\205\351\227\256\351\242\230.java" index 9f6d5e67616d6d4c8dcb575e044d74d154db37b7..6af18a80d070400bcc3718bf1af4cfd139adfe2c 100644 --- "a/ACWing/src/\346\240\221\345\275\242dp/\346\234\211\344\276\235\350\265\226\347\232\204\350\203\214\345\214\205\351\227\256\351\242\230.java" +++ "b/ACWing/src/\346\240\221\345\275\242dp/\346\234\211\344\276\235\350\265\226\347\232\204\350\203\214\345\214\205\351\227\256\351\242\230.java" @@ -72,6 +72,8 @@ public class 有依赖的背包问题 { for (int i = head[u]; i != 0; i = ne[i]) { int son = e[i]; dfs(e[i]); + //以u为根的子树,每一棵子树都是一个分组背包问题 + //抽象成2层 for (int j = m - v[u]; j >= 0; j--) {//枚举体积 for (int k = 0; k <= j; k++) {//枚举决策,分为以0~m的一些决策 f[u][j] = Math.max(f[u][j], f[u][j - k] + f[son][k]); diff --git "a/ACWing/src/\346\240\221\345\275\242dp/\346\240\221\347\232\204\347\233\264\345\276\204.java" "b/ACWing/src/\346\240\221\345\275\242dp/\346\240\221\347\232\204\347\233\264\345\276\204.java" index 18d52f60fa7bbe5fdc99d3ff885ba28d7cf2b5b9..f57b1eb16a1685cd88bf2feb01fcbe63fb34cfa7 100644 --- "a/ACWing/src/\346\240\221\345\275\242dp/\346\240\221\347\232\204\347\233\264\345\276\204.java" +++ "b/ACWing/src/\346\240\221\345\275\242dp/\346\240\221\347\232\204\347\233\264\345\276\204.java" @@ -1,7 +1,59 @@ package 树形dp; +import java.util.Scanner; + +/** + * 给定一棵树,树中包含 n个结点(编号1~n)和 n−1 条无向边,每条边都有一个权值。 + * 现在请你找到树中的一条最长路径。 + * 换句话说,要找到一条路径,使得使得路径两端的点的距离最远。 + * 注意:路径中可以只包含一个点。 + * 输入格式 + * 第一行包含整数 n。 + * 接下来 n−1行,每行包含三个整数 ai,bi,ci,表示点 ai和 bi 之间存在一条权值为 ci 的边。 + * 输出格式 + * 输出一个整数,表示树的最长路径的长度。 + * 数据范围 + * 1≤n≤10000 + * 1≤ai,bi≤n, + * −10^5≤ci≤10^5 + * 输入样例: + * 6 + * 5 1 6 + * 1 4 5 + * 6 3 9 + * 2 6 8 + * 6 1 7 + * 输出样例: + * 22 + */ public class 树的直径 { public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + n = sc.nextInt(); + int a, b, c; + for (int i = 0; i < n - 1; i++) { + a = sc.nextInt(); + b = sc.nextInt(); + c = sc.nextInt(); + add(b, a, c); + add(a, b, c); + } + } + + static void dfs(int u, int fa) { } -} + + static void add(int a, int b, int c) { + e[cnt] = b; + w[cnt] = c; + ne[cnt] = he[a]; + he[a] = cnt++; + } + + static int n, cnt = 1; + static int[] w = new int[10005]; + static int[] he = new int[10005]; + static int[] e = new int[10005]; + static int[] ne = new int[10005]; +} \ No newline at end of file diff --git "a/ACWing/src/\347\272\277\346\200\247dp/\345\244\247\347\233\227\351\230\277\347\246\217.java" "b/ACWing/src/\347\272\277\346\200\247dp/\345\244\247\347\233\227\351\230\277\347\246\217.java" new file mode 100644 index 0000000000000000000000000000000000000000..651c49f898167ae57bebedf2e0d8b7c13649fc69 --- /dev/null +++ "b/ACWing/src/\347\272\277\346\200\247dp/\345\244\247\347\233\227\351\230\277\347\246\217.java" @@ -0,0 +1,65 @@ +package 线性dp; + +import java.util.Arrays; +import java.util.Scanner; + +/** + * 阿福是一名经验丰富的大盗。趁着月黑风高,阿福打算今晚洗劫一条街上的店铺。 + * 这条街上一共有 N家店铺,每家店中都有一些现金。 + * 阿福事先调查得知,只有当他同时洗劫了两家相邻的店铺时,街上的报警系统才会启动,然后警察就会蜂拥而至。 + * 作为一向谨慎作案的大盗,阿福不愿意冒着被警察追捕的风险行窃。 + * 他想知道,在不惊动警察的情况下,他今晚最多可以得到多少现金? + * 输入的第一行是一个整数 T,表示一共有 T组数据。 + * 接下来的每组数据,第一行是一个整数 N,表示一共有 N家店铺。 + * 第二行是 N个被空格分开的正整数,表示每一家店铺中的现金数量。 + * 每家店铺中的现金数量均不超过1000。 + * 输出格式 + * 对于每组数据,输出一行。 + * 该行包含一个整数,表示阿福在不惊动警察的情况下可以得到的现金数量。 + * 数据范围 + * 1≤T≤50, + * 1≤N≤10^5 + * 输入样例: + * 2 + * 3 + * 1 8 2 + * 4 + * 10 7 6 14 + * 输出样例: + * 8 + * 24 + * 样例解释 + * 对于第一组样例,阿福选择第2家店铺行窃,获得的现金数量为8。 + * 对于第二组样例,阿福选择第1和4家店铺行窃,获得的现金数量为10+14=24。 + * 分析: + * 方法一:线性DP + * 作为一个典型的线性dp问题,本题可以用线性DP的解法来解决。 + * 状态表示f[i]表示在前i家店铺中能够获得的最多的现金。 + * 如果第i家店铺不洗劫,则获得的现金与在前i-1家店铺获得的最大现金一致, + * 即f[i] = f[i-1];如果第i家店铺洗劫,则第i-1家店铺不能洗劫, + * 否则会触发报警器,故此时f[i] = f[i-2] + w,w表示洗劫第i家店铺可以获得的现金。 + * 故状态转移方程为f[i] = max(f[i-1],f[i-2]+w),i >= 2。 + * 这里的边界状态为f[0] = 0,f[1] = w1,因为只有一家商铺时选择洗劫必然是最优选择。 + */ +public class 大盗阿福 { + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + t = sc.nextInt(); + while (t-- != 0) { + n = sc.nextInt(); + Arrays.fill(dp, 0); + for (int i = 0; i < n; i++) { + a[i] = sc.nextInt(); + } + dp[1] = a[0]; + for (int i = 2; i <= n; i++) { + dp[i] = Math.max(dp[i - 1], dp[i - 2] + a[i - 1]); + } + System.out.println(dp[n]); + } + } + + static int[] dp = new int[100050]; + static int[] a = new int[100050]; + static int t, n; +} diff --git "a/ACWing/src/\347\272\277\346\200\247dp/\345\255\220\344\270\262\345\222\214\345\255\220\345\272\217\345\210\227.java" "b/ACWing/src/\347\272\277\346\200\247dp/\345\255\220\344\270\262\345\222\214\345\255\220\345\272\217\345\210\227.java" new file mode 100644 index 0000000000000000000000000000000000000000..64fd1e96b00667dbd146ebc0d70e3f8c3c70730a --- /dev/null +++ "b/ACWing/src/\347\272\277\346\200\247dp/\345\255\220\344\270\262\345\222\214\345\255\220\345\272\217\345\210\227.java" @@ -0,0 +1,21 @@ +package 线性dp; + +import java.util.Scanner; + +public class 子串和子序列 { + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + S = sc.next(); + T = sc.next(); + for (int i = 0; i < S.length(); i++) { + for (int j = 0; j < T.length(); j++) { + if (S.charAt(i) == T.charAt(j)) + f[i + 1][i + 1][j + 1] = 1; + } + } + + } + + static int[][][] f = new int[5010][5010][5010]; + static String S, T; +} diff --git "a/ACWing/src/\347\272\277\346\200\247dp/\347\274\226\350\276\221\350\267\235\347\246\273.java" "b/ACWing/src/\347\272\277\346\200\247dp/\347\274\226\350\276\221\350\267\235\347\246\273.java" new file mode 100644 index 0000000000000000000000000000000000000000..0515716ca61ff9aa69794fa0e6a83d96b766d6f2 --- /dev/null +++ "b/ACWing/src/\347\272\277\346\200\247dp/\347\274\226\350\276\221\350\267\235\347\246\273.java" @@ -0,0 +1,37 @@ +package 线性dp; + +/** + * 给定n个长度不超过10的字符串以及m次询问,每次询问给出一个字符串和一个操作次数上限。 + * 对于每次询问,请你求出给定的n个字符串中有多少个字符串可以在上限操作次数内经过操作变成询问给出的字符串。 + * 每个对字符串进行的单个字符的插入、删除或替换算作一次操作。 + * 输入格式 + * 第一行包含两个整数n和m。 + * 接下来n行,每行包含一个字符串,表示给定的字符串。 + * 再接下来m行,每行包含一个字符串和一个整数,表示一次询问。 + * 字符串中只包含小写字母,且长度均不超过10。 + * 输出格式 + * 输出共m行,每行输出一个整数作为结果,表示一次询问中满足条件的字符串个数。 + * 数据范围 + * 1≤n,m≤1000, + * 输入样例: + * 3 2 + * abc + * acd + * bcd + * ab 1 + * acbd 2 + * 输出样例: + * 1 + * 3 + * 分析: + * 本题可直接调用AcWing 902 最短编辑距离中的算法框架。 + * 要求n个字符串中有多少个字符串可以在给定次数内转换成指定的m个字符串, + * 一共需要调用求最短编辑距离函数nm次,由于字符串长度不超过10, + * 故单次求最短编辑距离复杂度为100, + * 一共是1000*1000*100=10^8次,可以在2s内完成。 + */ +public class 编辑距离 { + public static void main(String[] args) { + + } +} diff --git "a/ACWing/src/\351\200\222\345\275\222/m\344\273\273\345\217\226n\346\273\241\350\266\263.java" "b/ACWing/src/\351\200\222\345\275\222/m\344\273\273\345\217\226n\346\273\241\350\266\263.java" index 4fae749d3697adc384fd0a399f0352b19c369147..10ee1a5be8423ed5b926652dab6a4b88f9abba2b 100644 --- "a/ACWing/src/\351\200\222\345\275\222/m\344\273\273\345\217\226n\346\273\241\350\266\263.java" +++ "b/ACWing/src/\351\200\222\345\275\222/m\344\273\273\345\217\226n\346\273\241\350\266\263.java" @@ -21,12 +21,11 @@ public class m任取n满足 { f(u + 1, sum + 1, s - arr[u]); } - static boolean x[] = new boolean[6]; + static boolean[] x = new boolean[6]; //搜索策略 static void dfs(int u, int cnt) { - for (int i = 0; i < n; i++) { if (!x[u]) { x[u] = true; diff --git "a/\347\256\227\346\263\225\345\276\210\347\276\216/src/dp/\350\203\226\350\200\201\351\274\240.java" "b/\347\256\227\346\263\225\345\276\210\347\276\216/src/dp/\350\203\226\350\200\201\351\274\240.java" index 81405a36de10f50875b2792c5adb0a1a3be44cbe..b3e01a0185633670093484f5df4862a0be23a2dc 100644 --- "a/\347\256\227\346\263\225\345\276\210\347\276\216/src/dp/\350\203\226\350\200\201\351\274\240.java" +++ "b/\347\256\227\346\263\225\345\276\210\347\276\216/src/dp/\350\203\226\350\200\201\351\274\240.java" @@ -1,9 +1,6 @@ package dp; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Scanner; +import java.util.*; /** * FatMouse's Speed @@ -141,6 +138,5 @@ public class 胖老鼠 { }); int[] dp = new int[list.size() + 1]; ArrayList mem = new ArrayList(); - } }