提交 e81f739f 编写于 作者: qq_36480062's avatar qq_36480062

c

上级 de2c9d18
package BFS;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
/**
* 给定一个n个点m条边的有向图,图中可能存在重边和自环。
* 所有边的长度都是1,点的编号为1~n。
* 请你求出1号点到n号点的最短距离,如果从1号点无法走到n号点,输出-1。
* 输入格式
* 第一行包含两个整数n和m。
* 接下来m行,每行包含两个整数a和b,表示存在一条从a走到b的长度为1的边。
* 输出格式
* 输出一个整数,表示1号点到n号点的最短距离。
* 数据范围
* 1≤n,m≤10^5
* 输入样例:
* 4 5
* 1 2
* 2 3
* 3 4
* 1 3
* 1 4
* 输出样例:
* 1
*/
public class 图中点的层次 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
for (int i = 0; i < m; i++) {
add(sc.nextInt(), sc.nextInt());
}
dis[n] = -1;
bfs(1);
System.out.println(dis[n]);
}
static int[] head = new int[(int) (1e5 + 10)];
static int[] ne = new int[(int) (1e5 + 10)];
static int[] e = new int[(int) (1e5 + 10)];
static int[] dis = new int[(int) (1e5 + 10)];
static boolean[] vis = new boolean[(int) (1e5 + 10)];
static int cnt = 1, n, m;
static Queue<Integer> q = new LinkedList<Integer>();
static void add(int u, int v) {
e[cnt] = v;
ne[cnt] = head[u];
head[u] = cnt++;
}
static void bfs(int u) {
vis[u] = true;
dis[u] = 0;
q.add(u);
int v, w;
while (!q.isEmpty()) {
v = q.poll();
for (int i = head[v]; i != 0; i = ne[i]) {
w = e[i];
if (!vis[w]) {
vis[w] = true;
q.add(w);
dis[w] = dis[v] + 1;
if (w == n) return;
//如果找到了就直接返回
}
}
}
}
}
package DFS;
import java.util.Scanner;
public class 树的重心 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
}
static int n, m;
}
......@@ -3,7 +3,7 @@ package RMQ;
import java.util.Random;
/**
* https://blog.csdn.net/qq_41311604/article/de tails/79900893
* https://blog.csdn.net/qq_41311604/article/details/79900893
* 只给出模板
* dp[i][j] = min(dp [i][j - 1], dp [i + (1 << j - 1)][j - 1])
* 由此给出下列代码:
......
......@@ -56,4 +56,4 @@ public class ST表最优写法 {
int k = log[r - l + 1];
return Math.min(st[l][k], st[r - (1 << k) + 1][k]);
}
}
}
\ No newline at end of file
package 前缀和差分;
public class 区间合并 {
public static void main(String[] args) {
}
}
package 前缀和差分;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
/**
* 坐标范围在10^9,需要使用离散化
* 值域跨度非常大,但非常稀疏,可以用离散化
*/
public class 区间和 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
int x, c;
for (int i = 0; i < n; i++) {
x = sc.nextInt();
c = sc.nextInt();
add.add(new node(x, c));
all.add(x);
}
int l, r;
for (int i = 0; i < m; i++) {
l = sc.nextInt();
r = sc.nextInt();
query.add(new node(l, r));
all.add(l);
all.add(r);
}
Collections.sort(all);
u();
for (node t : add) {
int at = find(t.x);
a[at] += t.y;
}
for (int i = 1; i <= all.size(); i++) {
s[i] = s[i - 1] + a[i];
}
for (node t : query) {
l = find(t.x);
r = find(t.y);
System.out.println(s[r] - s[l - 1]);
}
}
static class node {
int x, y;
public node(int x, int y) {
this.x = x;
this.y = y;
}
}
static int find(int x) {
int l = 0, r = all.size() - 1, mid;
while (l < r) {
mid = l + r >> 1;
if (all.get(mid) >= x) r = mid;
else l = mid + 1;
}
return r + 1;
}
static ArrayList<Integer> all = new ArrayList<Integer>();
static ArrayList<node> add = new ArrayList<node>(), query = new ArrayList<node>();
static int n, m;
static int[] s = new int[(int) (3e5 + 10)], a = new int[(int) (3e5 + 10)];
static int unique(int[] a) {
int j = 0;
for (int i = 0; i < a.length; i++) {
if (i == 0 || a[i] != a[i - 1]) {
a[j++] = a[i];
}
}
return j;
}
/**
* 双指针
* 去重
* 1 1 2 2 3 4 5 5
* 他是第一个
* a[i]≠a[i-1]
* 满足该性质的即是去重后的元素
*/
static void u() {
int j = 0;
for (int i = 0; i < all.size(); i++) {
if (i == 0 || !all.get(i).equals(all.get(i - 1))) {
all.set(j++, all.get(i));
}
}
}
}
package 前缀和差分;
import java.util.TreeSet;
/**
* a[i] 1 3 100 200 50000
* 0 1 2 3 4
* 算出x离散化后的值,二分
* a[i]中应该去重有序
*/
public class 离散化 {
public static void main(String[] args) {
for (int i : arr) {
t.add(i);
}
int c = 0;
for (Integer w : t) {
arr[c++] = w;
}
System.out.println(find(8));
}
static TreeSet<Integer> t = new TreeSet<Integer>();
static int[] arr = {2, 5, 1, 7, 9, 2};
static int find(int x) {
int l = 0, r = t.size() - 1;
while (l < r) {
int mid = l + r >> 1;
if (arr[mid] >= x) r = mid;
else l = mid + 1;
}
return r;
}
}
package 区间dp;
import java.util.Scanner;
/**
* 设有N堆石子排成一排,其编号为1,2,3,…,N。
* 每堆石子有一定的质量,可以用一个整数来描述,现在要将这N堆石子合并成为一堆。
* 每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同。
* 例如有4堆石子分别为 1 3 5 2, 我们可以先合并1、2堆,代价为4,得到4 5 2, 又合并 1,2堆,代价为9,得到9 2 ,再合并得到11,总代价为4+9+11=24;
* 如果第二步是先合并2,3堆,则代价为7,得到4 7,最后一次合并代价为11,总代价为4+7+11=22。
* 问题是:找出一种合理的方法,使总的代价最小,输出最小代价。
* 输入格式
* 第一行一个数N表示石子的堆数N。
* 第二行N个数,表示每堆石子的质量(均不超过1000)。
* 输出格式
* 输出一个整数,表示最小代价。
* 数据范围
* 1≤N≤300
* 输入样例:
* 4
* 1 3 5 2
* 输出样例:
* 22
* 状态定义:f[i,j]是合并i~j位所需的最小代价的集合
* 属性:min 最小代价
* 状态划分:f[i,j]划分,i i+1 ... k ... j-1
* ___ _____
* 起码有两堆,不失一般性,考虑k,i~k k+1~j
* i~k合并的最小代价结合状态定义恰好是f[i,k]
* k+1,j合并的最小代价结合状态定义恰好是f[k+1,j]
* 最后加上他们的和 即可:s[j]-s[i-1]
*/
public class 石子合并 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
for (int i = 1; i <= n; i++) {
s[i] = sc.nextInt();
s[i] += s[i - 1];
}//前缀和
for (int len = 2; len <= n; len++) {
for (int i = 1; i + len - 1 <= n; i++) {
int j = i + len - 1;
f[i][j] = (int) 1e9;
for (int k = i; k < j; k++) {
f[i][j] = Math.min(f[i][j], f[i][k] + f[k + 1][j] + s[j] - s[i - 1]);
}
}
}
System.out.println(f[1][n]);
}
static int[] s = new int[310];
static int[][] f = new int[310][310];
static int n = 0;
}
package 数学;
import java.util.Scanner;
//统计一个数二进制有多少个1
public class 位运算 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
int res = 0;
while (n != 0) {
n -= lowbit(n);
res++;
}
System.out.println(res);
}
private static int lowbit(int n) {
return n&-n;
}
static int n;
}
......@@ -2,8 +2,6 @@ package 数学;
import java.util.Scanner;
import static java.lang.System.in;
/**
* 90. 64位整数乘法
* 求 a 乘 b 对 p 取模的值。
......@@ -30,12 +28,21 @@ import static java.lang.System.in;
*/
public class 快速乘 {
public static void main(String[] args) {
Scanner sc = new Scanner(in);
Scanner sc = new Scanner(System.in);
long a = sc.nextLong();
long b = sc.nextLong();
long p = sc.nextLong();
System.out.println(ks(a, b, p));
System.out.println(ca(2, 20));
}
//递归快速乘
static int ca(int a, int b) {
int res = 0;
if (b == 0) return res;
if ((b & 1) == 1) {
res += a;
}
return res + ca(a << 1, b >> 1);
}
//转换为快速加法,a*b
......@@ -52,4 +59,6 @@ public class 快速乘 {
}
return res;
}
}
......@@ -13,8 +13,10 @@ import java.util.Scanner;
* 集合划分:子树1,子树3,子树3
* 再根据体积划分子树:体积是0-m
* 把每一颗子树看做物品组,分组背包问题
* 链式前向星建图
*/
public class 有依赖的背包问题 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
......@@ -22,5 +24,14 @@ public class 有依赖的背包问题 {
}
static int n, v;
static int N = 110;
static int[] head = new int[N], e = new int[N], ne = new int[N];
static int n, v, cnt = 1;
static void add(int a, int b) {
e[cnt] = b;
ne[cnt] = head[a];
head[a] = cnt++;
}
}
package 树形dp;
import java.util.Scanner;
public class 没有上司的舞会 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
for (int i = 1; i <= n; i++) {
happy[i] = sc.nextInt();
}
int a, b;
for (int i = 0; i < n - 1; i++) {
a = sc.nextInt();
b = sc.nextInt();
add(b, a);
hasf[a] = true;
}
int root = 1;
while (hasf[root]) root++;
dfs(root);
System.out.println(Math.max(f[root][1], f[root][0]));
}
private static void dfs(int u) {
f[u][1] = happy[u];//选择了u就加上u的幸福指数
for (int i = he[u]; i != 0; i = ne[i]) {
int j = e[i];
dfs(j);
f[u][1] += f[j][0];//不选子节点
f[u][0] += Math.max(f[j][0], f[j][1]);
//不选当前节点,是选还是不选子节点
}
}
static int[][] f = new int[6010][6010];
static void add(int u, int v) {
e[cnt] = v;
ne[cnt] = he[u];
he[u] = cnt++;
}
static int n, cnt = 1;
static int[] he = new int[6010];
static int[] ne = new int[6010];
static int[] e = new int[6010];
static int[] happy = new int[6010];
static boolean[] hasf = new boolean[6010];
}
package 线性dp;
import java.util.Scanner;
/**
* https://blog.csdn.net/qq_30277239/article/details/103973199
* 给定两个字符串A和B,现在要将A经过若干操作变为B,可进行的操作有:
* 删除–将字符串A中的某个字符删除。
* 插入–在字符串A的某个位置插入某个字符。
* 替换–将字符串A中的某个字符替换为另一个字符。
* 现在请你求出,将A变为B至少需要进行多少次操作。
* 输入格式
* 第一行包含整数n,表示字符串A的长度。
* 第二行包含一个长度为n的字符串A。
* 第三行包含整数m,表示字符串B的长度。
* 第四行包含一个长度为m的字符串B。
* 字符串中均只包含大写字母。
* 输出格式
* 输出一个整数,表示最少操作次数。
* 数据范围
* 1≤n,m≤1000
* 输入样例:
* 10
* AGTCTGACGC
* 11
* AGTAAGTAGGC
* 输出样例:
* 4
* 本题要求将A转化为B的最小操作次数,状态表示:f[i][j]表示将A[1~i]转化为B[1~j]的最少操作次数。
* 考察f[i][j]的状态可以由哪些状态转移而来?无非是由某种状态进行一步插入、删除或者替换操作,
* 可将之前的状态转化为f[i][j],我们要思考的是如何由规模更小的子问题通过一步操作得到现在的状态。
* 如果最后一步操作是插入一个元素,在此之前我们知道将A[1~i]转化为B[1~j-1]的最小操作次数f[i][j-1],
* 再插入B[j]即可使得A与B匹配,则f[i][j] = f[i][j-1] + 1;如果最后一步操作是删除A[i],
* 在此之前A[1~i-1]与B[1~j]应该是匹配的,则f[i][j] = f[i-1][j] + 1,;
* 如果最后一步操作是修改A[i],则A[i] == B[j]时,这步不用操作,即f[i][j] = f[i-1][j-1];
* A[i] != B[j]时,需要修改A[i],即f[i][j] = f[i-1][j-1] + 1。
* 在我们知道了f[i-1][j],f[i][j-1]以及f[i-1][j-1]后,
* 我们可以尝试从这三种状态的每一种状态转化成f[i][j]来计算出最小操作次数,即A[i] == B[j]时
* ,f[i][j] = f[i-1][j-1];A[i] != B[j]时,f[i][j] = min(f[i][j-1],f[i-1][j],f[i-1][j-1]) + 1。
* 最后考虑边界情况,f[0][j]表示从无到B[1~j]的最小操作次数,显然是j次插入操作;
* f[i][0]表示将A[1~i]清空,显然是i次删除操作。
* 状态定义:f[i,j]代表1~i范围内转换为1~j范围内的所有转换方式,
* 属性,min最小值
* 边界情况f[i,0]:i个字符转换成0个字符
* for(0~i)
* f[i,0]=i
* f[0,j]:0个字符转换成j个字符
* for(0~j)
* f[0,j]=j
* 集合划分:分4种情况f[i,j] 考虑last
* 前三种情况:当a[i]!=b[j]
* 1.通过增加一个字符变成b
* ____ a
* ____ +一个字符 b
* 说明前面1~i个字符与j-1匹配
* 求前面的最小值,范围是1~i-1 那j的范围还是j
* 则是:f[i][j-1]+1 :前1~i-1个字符转换成j的最小步数
* 2.通过删除一个字符变成b:
* _____ a
* ____ b
* 说明前1~i-1个字符与前j个字符匹配
* 则是由f[i-1][j]转移过来的
* f[i][j]=f[i-1][j]+1
* 3. 通过修改其中一个元素达成
* _____ a
* _____ b
* 说明前面1~i-1和1~j-1是匹配的
* 则:f[i][j]=f[i-1][j-1]+1
* 4. 如果相等a[i]==b[j]
* 则无需转换:f[i][j]=f[i-1][j-1]
* 最终转移方程if (a[i]!=b[j])
* f[i][j]=min( f[i-1][j], f[i][j-1] f[i-1][j-1] ) +1
* if ( a[i]==b[j] )
* f[i][j]=f[i-1][j-1]
*/
public class 最短编辑距离 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
a = sc.next().toCharArray();
m = sc.nextInt();
b = sc.next().toCharArray();
for (int i = 0; i <= n; i++) f[i][0] = i;
for (int i = 0; i <= m; i++) f[0][i] = i;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (a[i - 1] == b[j - 1]) f[i][j] = f[i - 1][j - 1];
else f[i][j] = Math.min(Math.min(f[i - 1][j], f[i][j - 1]), f[i - 1][j - 1]) + 1;
}
}
System.out.println(f[n][m]);
}
static int[][] f = new int[1005][1010];
static char[] a = new char[1010], b = new char[1010];
static int n, m;
}
package 贪心;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
//https://blog.csdn.net/qq_30277239/article/details/100880649?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522158685398819724845039319%2522%252C%2522scm%2522%253A%252220140713.130056874..%2522%257D&request_id=158685398819724845039319&biz_id=0&utm_source=distribute.pc_search_result.none-task-blog-blog_SOOPENSEARCH-1
/**
* 给定 n 个区间 [li,ri],要求合并所有有交集的区间。
* 注意如果在端点处相交,也算有交集。
* 输出合并完成后的区间个数。
* 例如:[1,3]和[2,6]可以合并为一个区间[1,6]。
* 第一行包含整数n。
* 接下来n行,每行包含两个整数 l 和 r。
* 输出格式
* 共一行,包含一个整数,表示合并区间完成后的区间个数。
* 数据范围
* 1≤n≤100000,
* −10^9≤li≤ri≤10^9
* 输入样例:
* 5
* 1 2
* 2 4
* 5 6
* 7 8
* 7 9
* 输出样例:
* 3
* 排序贪心,
* 本题考察贪心,将待合并的区间按左端点从小到大排序后,
* 维持两个变量l,r,每次遍历到下一个区间,其左端点肯定是大于l的,
* 若其左端点还大于r,则说明l-r的区间不会再与后面的区间进行合并了
* 因为后续区间的左端点都会大于l;反之,
* 若l小于右端点,则合并两个区间,并更新r。
*/
public class 区间合并 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
for (int i = 0; i < n; i++) {
segs.add(new node(sc.nextInt(), sc.nextInt()));
}
Collections.sort(segs);
megers();
}
static void meger() {
int st = (int) -2e9, ed = (int) -2e9;
int res = 0;
for (node seg : segs) {
if (ed < seg.x) {
if (st != -2e9) res++;
st = seg.x;
ed = seg.y;
} else ed = Math.max(ed, seg.y);
}
if (st != -2e9) res++;//最后一个没有更新
System.out.println(res);
}
static void megers() {
int st = segs.get(0).x;
int ed = segs.get(0).y;
int res = 1;
for (int i = 1; i < segs.size(); i++) {
if (ed < segs.get(i).x) {
st = segs.get(i).x;
ed = segs.get(i).y;
res++;
} else {
ed = Math.max(ed, segs.get(i).y);
}
}
System.out.println(res);
}
static class node implements Comparable<node> {
int x, y;
public node(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public int compareTo(node node) {
return x - node.x;
}
}
static int n;
static ArrayList<node> segs = new ArrayList<node>();
}
......@@ -9,7 +9,6 @@ import java.util.Comparator;
* 有n头牛,它们都有一个工作时间的区间s至e,
* 给定一个总的工作时间t,
* 问最少需要多少头牛才能覆盖从1到t的工作时间
* <p>
* 题目说人话就是:比如1点到10点需要有牛耕地,会给出,几点到几点需要工作
* 有的牛只在3点到4点工作,会给出每头牛在什么时间会工作
* 问最少需要多少头牛,才能在1-10点都有牛工作
......
package string;
import java.util.Arrays;
import java.util.Random;
/**
* KMP字符串匹配...未完
* KMP字符串匹配...
* https://blog.csdn.net/dark_cy/article/details/88698736
*/
public class KMP {
public static void main(String[] args) {
for (int i = 3; i < 1000; i++) {
String st = randomStr(i);
if (!Arrays.equals(next(st), nextt(st))) {
System.out.println(st);
System.out.println(Arrays.toString(next(st)));
System.out.println(Arrays.toString(nextt(st)));
String s = "214131242";
String t = "124";
int[] next = nextt(t);
int i = 0, j = 0;
while (i < s.length() && j < t.length()) {
if (j == -1 || s.charAt(i) == t.charAt(j)) {
i++;
j++;
} else j = next[j];
if (j >= t.length()) {
System.out.println(i-t.length());
}
}
System.out.println(Arrays.toString(next("abaabcac")));
System.out.println(Arrays.toString(nextt("abaabcac")));
}
......
......@@ -27,6 +27,21 @@ x的负数表示~x+1也可以直接-x
~x+1+x=0
用加法实现减法,所以使用补码来表示负数
1. 求n的二进制表示的第k位 n>>k&1
2.返回n的最后一位1 n&-n
3.双指针算法:
for(int i=0,j=0;i<n;i++){
while(j<i&&check(i,j))j++;
问题逻辑:
1.对于一个序列,用两个指针维护一段区间
2.对于两个序列:维护某种次序,比如归并排序的合并两个有序数列的操作
4. 离散化:
a[i] 1 3 100 200 50000
0 1 2 3 4
算出x离散化后的值,二分
a[i]中应该去重有序
}
````
###位运算的技巧
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册