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

c

上级 cd1d56a0
package UnionFind;
/**
* https://blog.csdn.net/weixin_44922845/article/details/104516620
* Alice和Bob玩了一个古老的游戏:首先画一个 n×n 的点阵(下图 n=3 )。
* 接着,他们两个轮流在相邻的点之间画上红边和蓝边:
* 直到围成一个封闭的圈(面积不必为 1)为止,“封圈”的那个人就是赢家。因为棋盘实在是太大了,他们的游戏实在是太长了!
* 他们甚至在游戏中都不知道谁赢得了游戏。
* 于是请你写一个程序,帮助他们计算他们是否结束了游戏?
* 输入格式
* 输入数据第一行为两个整数 n 和 m。n表示点阵的大小,m 表示一共画了 m 条线。
* 以后 m 行,每行首先有两个数字 (x,y),代表了画线的起点坐标,接着用空格隔开一个字符,假如字符是 D,则是向下连一条边,如果是 R 就是向右连一条边。
* 输入数据不会有重复的边且保证正确。
* 输出格式
* 输出一行:在第几步的时候结束。
* 假如 m 步之后也没有结束,则输出一行“draw”。
* 数据范围
* 1≤n≤2001≤m≤240001≤n≤200
* 1≤m≤24000
* 输入样例
* 3 5
* 1 1 D
* 1 1 R
* 1 2 D
* 2 1 R
* 2 2 D
* 输出样例
* 4
*/
public class 格子游戏 {
public static void main(String[] args) {
}
}
package basic.UnionFind; package UnionFind;
import java.util.Scanner; import java.util.Scanner;
/** /**
* https://blog.csdn.net/lisong_jerry/article/details/80029967?utm_medium=distribute.pc_relevant.none-task-blog-baidulandingword-2&spm=1001.2101.3001.4242
* 带权并查集 * 带权并查集
* 精髓:只要两个元素在一个集合里面, * 精髓:只要两个元素在一个集合里面,
* 通过它们与根节点的距离就能知道它们的相对关系 * 通过它们与根节点的距离就能知道它们的相对关系
......
...@@ -30,7 +30,7 @@ public class 括号匹配 { ...@@ -30,7 +30,7 @@ public class 括号匹配 {
if ((s[l] == '(' && s[r] == ')') || s[l] == '[' && s[r] == ']') { if ((s[l] == '(' && s[r] == ')') || s[l] == '[' && s[r] == ']') {
f[l][r] = f[l + 1][r - 1] + 2; f[l][r] = f[l + 1][r - 1] + 2;
}//查看头尾是否匹配 }//查看头尾是否匹配
//如([])([])从中间分界线,那么它们依然可以匹配,符合要求
//枚举小区间,推出大区间最长序列 //枚举小区间,推出大区间最长序列
for (int k = l; k < r; k++) { for (int k = l; k < r; k++) {
f[l][r] = Math.max(f[l][k] + f[k + 1][r], f[l][r]); f[l][r] = Math.max(f[l][k] + f[k + 1][r], f[l][r]);
......
package dp.数据结构优化dp;
import java.util.Scanner;
/**
* https://blog.csdn.net/Rose_max/article/details/82932210
* 给你一个长度为n的序列,要求你把它分成K段
* 每段的价值为这段的总权值%P
* 要求总价值最小
* n<=500000 K<=100 P<=100
* 4 3 10 4个数,分成3段,mod10最小
* 3 4 7 2
* out:
* 6
*
*/
public class Cf958C3分段序列 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
k = sc.nextInt();
m = sc.nextInt();
}
static int n, k, m;
}
package dp.状态压缩dp;
public class 放骨牌蒙德里安的梦想另一种解法 {
public static void main(String[] args) {
}
static void dfs(int i, int now, int pre) {
}
}
...@@ -6,6 +6,13 @@ package graph.Floyd; ...@@ -6,6 +6,13 @@ package graph.Floyd;
*/ */
public class 牛的旅行 { public class 牛的旅行 {
public static void main(String[] args) { public static void main(String[] args) {
long i = 0;
long s = System.nanoTime();
long t = 0;
for (i = 0; ; i++) {
if (i % 100000 == 0 && (System.nanoTime() - s) / 1e9 >= 0.98)
break;
}
System.out.println(i);
} }
} }
...@@ -55,7 +55,7 @@ public class dijkstra { ...@@ -55,7 +55,7 @@ public class dijkstra {
c = nextInt(); c = nextInt();
add(a, b, c); add(a, b, c);
} }
Arrays.fill(dis, (1 << 31) - 1); Arrays.fill(dis, (1 << 31));
dij(s); dij(s);
} }
......
...@@ -36,6 +36,7 @@ import java.util.Scanner; ...@@ -36,6 +36,7 @@ import java.util.Scanner;
* 初始化为int/2即可 * 初始化为int/2即可
* 即使全是负权边,int/2-2*10^8,无穷-常数还是无穷 * 即使全是负权边,int/2-2*10^8,无穷-常数还是无穷
* 如何判别无穷呢,显然 int/4>2*10^8 * 如何判别无穷呢,显然 int/4>2*10^8
*
*/ */
public class floyd { public class floyd {
public static void main(String[] args) { public static void main(String[] args) {
......
...@@ -54,24 +54,22 @@ public class spfa判断负环 { ...@@ -54,24 +54,22 @@ public class spfa判断负环 {
// 原理:如果某条最短路径上有n个点(除了自己),那么加上自己之后一共有n+1个点,由抽屉原理一定有两个点相同,所以存在环。 // 原理:如果某条最短路径上有n个点(除了自己),那么加上自己之后一共有n+1个点,由抽屉原理一定有两个点相同,所以存在环。
ArrayDeque<Integer> q = new ArrayDeque<Integer>(); ArrayDeque<Integer> q = new ArrayDeque<Integer>();
for (int i = 1; i <= n; i++) { for (int i = 1; i <= n; i++) {
q.add(i); q.add(i);//假设超级源点连接每个点权值都为0
vis[i] = true; st[i] = true;
} }
int t = 0, x; int t = 0, x;
while (!q.isEmpty()) { while (!q.isEmpty()) {
x = q.poll(); x = q.poll();
vis[x] = false; st[x] = false;
for (int i = he[x]; i != 0; i = ne[i]) { for (int i = he[x]; i != 0; i = ne[i]) {
t = e[i]; t = e[i];
if (dis[t] > dis[x] + w[i]) { if (dis[t] > dis[x] + w[i]) {
dis[t] = dis[x] + w[i];
count[t] = count[x] + 1; count[t] = count[x] + 1;
dis[t] = dis[x] + w[i];
if (count[t] >= n) return true; if (count[t] >= n) return true;
if (!vis[t]) { if (!st[t]) {
if (!q.isEmpty() && dis[t] < dis[q.peekFirst()]) { q.add(t);
q.addFirst(t); st[t] = true;
} else q.add(t);
vis[t] = true;
} }
} }
} }
...@@ -81,7 +79,7 @@ public class spfa判断负环 { ...@@ -81,7 +79,7 @@ public class spfa判断负环 {
static int[] count = new int[100005]; static int[] count = new int[100005];
static int[] dis = new int[100005]; static int[] dis = new int[100005];
static boolean[] vis = new boolean[100005]; static boolean[] st = new boolean[100005];
static int[] he = new int[100005]; static int[] he = new int[100005];
static int[] w = new int[200005]; static int[] w = new int[200005];
static int[] ne = new int[200005]; static int[] ne = new int[200005];
......
...@@ -11,6 +11,7 @@ import java.util.Scanner; ...@@ -11,6 +11,7 @@ import java.util.Scanner;
* 寻找最短路 * 寻找最短路
* 等级问题的话,枚举等级区间 * 等级问题的话,枚举等级区间
*/ */
@SuppressWarnings("all")
public class 昂贵的聘礼 { public class 昂贵的聘礼 {
public static void main(String[] args) { public static void main(String[] args) {
Scanner sc = new Scanner(System.in); Scanner sc = new Scanner(System.in);
...@@ -33,13 +34,24 @@ public class 昂贵的聘礼 { ...@@ -33,13 +34,24 @@ public class 昂贵的聘礼 {
w[id][i] = Math.min(w[id][i], cost); w[id][i] = Math.min(w[id][i], cost);
} }
} }
/**
* 因为最终要娶到公主,所以等级只能只能在level[1]也就是酋长的等级
* level[1]-m ~ level[1]+m 这个区间内
*/
int res = Integer.MAX_VALUE / 2; int res = Integer.MAX_VALUE / 2;
for (int i = level[1] - m; i <= level[1]; i++) { for (int i = level[1] - m; i <= level[1]; i++) {
res = Math.min(res, dijkstra(i, i + m)); res = Math.min(res, dijkstra(i, i + m));
} }
System.out.println(res); System.out.println(res);
} }
/**
* 枚举等级区间!来实现等级差距过大无法交易
*
* @param down
* @param up
* @return
*/
static int dijkstra(int down, int up) { static int dijkstra(int down, int up) {
Arrays.fill(dist, 0x3f3f3f3f); Arrays.fill(dist, 0x3f3f3f3f);
Arrays.fill(vis, false); Arrays.fill(vis, false);
......
...@@ -21,6 +21,7 @@ import java.util.Scanner; ...@@ -21,6 +21,7 @@ import java.util.Scanner;
* 2 * 2
* 建图,问题, * 建图,问题,
* 2 3 4 5可以看做2-3 2-4 2-5 3-4 3-5 4-5 * 2 3 4 5可以看做2-3 2-4 2-5 3-4 3-5 4-5
* 抽象建图!想法很重要!
* 显然:2可以直接到3,2也可以直接到4,2也可以直接到5 * 显然:2可以直接到3,2也可以直接到4,2也可以直接到5
* 边权都为1可以使用bfs做 * 边权都为1可以使用bfs做
* 则显然求最小换乘次数,转换成求最短路径问题 * 则显然求最小换乘次数,转换成求最短路径问题
...@@ -49,7 +50,6 @@ public class 最优乘车 { ...@@ -49,7 +50,6 @@ public class 最优乘车 {
static int[][] g = new int[510][510]; static int[][] g = new int[510][510];
static int[] dist = new int[510]; static int[] dist = new int[510];
static int n, m; static int n, m;
static void bfs() { static void bfs() {
......
package graph.单源最短路拓展;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Scanner;
/**
* https://blog.csdn.net/qq_30277239/article/details/106743915
* 有一天,琪琪想乘坐公交车去拜访她的一位朋友。
* 由于琪琪非常容易晕车,所以她想尽快到达朋友家。
* 现在给定你一张城市交通路线图,上面包含城市的公交站台以及公交线路的具体分布。
* 已知城市中共包含 n 个车站(编号1~n)以及 m 条公交线路。
* 每条公交线路都是 单向的,从一个车站出发直接到达另一个车站,两个车站之间可能存在多条公交线路。
* 琪琪的朋友住在 s 号车站附近。
* 琪琪可以在任何车站选择换乘其它公共汽车。
* 请找出琪琪到达她的朋友家(附近的公交车站)需要花费的最少时间。
* 输入格式
* 输入包含多组测试数据。
* 每组测试数据第一行包含三个整数 n,m,s,分别表示车站数量,公交线路数量以及朋友家附近车站的编号。
* 接下来 m 行,每x行包含三个整数 p,q,t,表示存在一条线路从车站 p 到达车站 q,用时为 t。
* 接下来一行,包含一个整数 w,表示琪琪家附近共有 w 个车站,她可以在这 w 个车站中选择一个车站作为始发站。
* 再一行,包含 w 个整数,表示琪琪家附近的 w 个车站的编号。
* 输出格式
* 每个测试数据输出一个整数作为结果,表示所需花费的最少时间。
* 如果无法达到朋友家的车站,则输出 -1。
* 每个结果占一行。
* 数据范围
* n≤1000,m≤20000,
* 1≤s≤n,
* 0<w<n
* 0<t≤1000
* 输入样例:
* 5 8 5
* 1 2 2
* 1 5 3
* 1 3 4
* 2 4 7
* 2 5 6
* 2 3 5
* 3 5 1
* 4 5 1
* 2
* 2 3
* 4 3 4
* 1 2 3
* 1 3 4
* 2 3 2
* 1
* 1
* 输出样例:
* 1
* -1
* 任选一个起点,到达终点,虚拟源点,是很重要的技巧
* 该题,可以建立反向边,求终点到每个起点,遍历每个起点哪个到终点距离更短,但不具拓展性
* 多个起点多个终点,考虑虚拟源点
*/
public class 选择最佳线路 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int a, b, c;
while (sc.hasNext()) {
cnt = 1;
Arrays.fill(st, false);
n = sc.nextInt();
m = sc.nextInt();
T = sc.nextInt();
while (m-- != 0) {
a = sc.nextInt();
b = sc.nextInt();
c = sc.nextInt();
add(a, b, c);
}
int s = sc.nextInt();
while (s-- != 0) {
int ver = sc.nextInt();
add(0, ver, 0);
}
System.out.println(spfa());
}
}
static int spfa() {
Arrays.fill(dis, inf);
dis[0] = 0;
ArrayDeque<Integer> q = new ArrayDeque<Integer>();
q.add(0);
while (!q.isEmpty()) {
int t = q.poll();
st[t] = false;
for (int i = h[t]; i != 0; i = ne[i]) {
int j = e[i];
if (dis[j] > dis[t] + w[i]) {
dis[j] = dis[t] + w[i];
if (!st[j]) {
q.add(j);
st[j] = true;
}
}
}
}
if (dis[T] == inf) return -1;
else return dis[T];
}
static int inf = Integer.MAX_VALUE / 2;
static void add(int a, int b, int c) {
e[cnt] = b;
w[cnt] = c;
ne[cnt] = h[a];
h[a] = cnt++;
}
static int n, m, T, N = 1100, M = 22010, cnt = 1;
static int[] e = new int[M];
static int[] h = new int[N];
static int[] w = new int[M];
static int[] ne = new int[M];
static int[] dis = new int[N];
static boolean[] st = new boolean[N];
}
package graph.复合单源最短路; package graph.复合单源最短路;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Scanner; import java.util.Scanner;
/** /**
* https://blog.csdn.net/qq_30277239/article/details/106629032
* 农夫约翰正在一个新的销售区域对他的牛奶销售方案进行调查。
* 他想把牛奶送到T个城镇,编号为1~T。
* 这些城镇之间通过R条道路 (编号为1到R) 和P条航线 (编号为1到P) 连接。
* 每条道路 i 或者航线 i 连接城镇Ai到Bi,花费为Ci。
* 对于道路,0≤Ci≤10,000;然而航线的花费很神奇,花费Ci可能是负数(−10,000≤Ci≤10,000)。
* 道路是双向的,可以从Ai到Bi,也可以从Bi到Ai,花费都是Ci。
* 然而航线与之不同,只可以从Ai到Bi。
* 事实上,由于最近恐怖主义太嚣张,为了社会和谐,出台了一些政策:
* 保证如果有一条航线可以从Ai到Bi,那么保证不可能通过一些道路和航线从Bi回到Ai。
* 由于约翰的奶牛世界公认十分给力,他需要运送奶牛到每一个城镇。
* 他想找到从发送中心城镇S把奶牛送到每个城镇的最便宜的方案。
* 输入格式
* 第一行包含四个整数T,R,P,S。
* 接下来R行,每行包含三个整数(表示一个道路)Ai,Bi,Ci。
* 接下来P行,每行包含三个整数(表示一条航线)Ai,Bi,Ci。
* 输出格式
* 第1..T行:第i行输出从S到达城镇i的最小花费,如果不存在,则输出“NO PATH”。
* 数据范围
* 1≤T≤25000,
* 1≤R,P≤50000,
* 1≤Ai,Bi,S≤T,
* 输入样例:
* 6 3 3 4
* 1 2 5
* 3 4 5
* 5 6 10
* 3 5 -100
* 4 6 -100
* 1 3 -10
* 输出样例:
* NO PATH
* NO PATH
* 5
* 0
* -95
* -100
* 如果边权非负使用Dijkstra算法 * 如果边权非负使用Dijkstra算法
* spfa会被卡 * spfa会被卡
* 把双向的非负块节点,用Dijkstra算法 * 把双向的非负块节点,用Dijkstra算法
* 把一群节点,看做一个节点块 * 把一群节点,看做一个节点块,按照拓扑序跑
*/ */
public class 道路与航路 { public class 道路与航路 {
public static void main(String[] args) { public static void main(String[] args) {
Scanner sc = new Scanner(System.in); Scanner sc = new Scanner(System.in);
for (int i = 0; i < block.length; i++) {
block[i] = new ArrayList<Integer>();
}
n = sc.nextInt();
mr = sc.nextInt();
mp = sc.nextInt();
S = sc.nextInt();
int a, b, c;
while (mr-- != 0) {
a = sc.nextInt();
b = sc.nextInt();
c = sc.nextInt();
add(a, b, c);
add(b, a, c);
}
for (int i = 1; i <= n; i++) {
if (id[i] == 0) {
dfs(i, ++bcnt);
}
}
while (mp-- != 0) {
a = sc.nextInt();
b = sc.nextInt();
c = sc.nextInt();
add(a, b, c);
}
} }
static ArrayDeque<Integer> q = new ArrayDeque<Integer>();
private static void dfs(int u, int bid) {
id[u] = bid;
block[bid].add(u);
for (int i = h[u]; i != 0; i = ne[i]) {
int j = e[i];
if (id[j] == 0)
dfs(j, bid);
}
}
private static void add(int a, int b, int c) {
e[cnt] = b;
w[cnt] = c;
ne[cnt] = h[a];
h[a] = cnt++;
}
static int n, mr, mp, S, N = 25010, M = 150010, cnt = 1, bcnt = 0;
static int[] h = new int[N];
static int[] e = new int[M];
static int[] w = new int[M];
static int[] ne = new int[M];
static int[] id = new int[N];
static int[] dis = new int[N];
static boolean[] st = new boolean[N];
static ArrayList<Integer> block[] = new ArrayList[N];
} }
package graph.差分约束;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Scanner;
/**
* https://blog.csdn.net/qq_44828887/article/details/107272314
* 给定n个区间,[ai,bi]和n个整数Ci
* 你需要构造一个整数集合 Z,
* 使得∀i∈[1,n],Z 中满足ai≤x≤bi的整数 x 不少于 ci 个。
* 求这样的整数集合 Z 最少包含多少个数。
* 1≤n≤50000,
* 0≤ai,bi≤50000,
* 1≤ci≤bi−ai+1
* 输入样例:
* 5
* 3 7 3
* 8 10 3
* 6 8 1
* 1 3 1
* 10 11 1
* 输出样例:
* 6
* 该题必定有解,显然全放进去,一定满足要求
* 思路:
* 先将不等式写出来
* 将所有区间向右移一位,这样如果1-2有一条边 3-4有一条边,
* 我们应该让他可以处理为1-4区间,
* 所以相当于给的区间为左闭右闭的区间改为左闭右开的区间。
* 初始的时候建立向右走1权值为0的边,向左走1权值为-1的边。
* 跑最长路即可。
* 列不等式:
* S0=0
* Si表示1~i中被选出的数的个数
* S5001的最小值,用最长路求解
* Si>=S(i-1) 1<=i<=50001
* Si-S(i-1)<=1 =>S(i-1)>=Si-1
* [a,b]区间选c个,也就是Sb-S(a-1)>=c
* 不等关系要找全
*/
public class 区间 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
for (int i = 1; i <= 50001; i++) {
add(i - 1, i, 0);
add(i, i - 1, -1);
}
int a, b, c;
while (n-- != 0) {
a = sc.nextInt();
b = sc.nextInt();
c = sc.nextInt();
a++;
b++;
add(a - 1, b, c);
}
spfa();
System.out.println(dis[50001]);
}
private static void spfa() {
q.add(0);
Arrays.fill(dis, -Integer.MAX_VALUE / 2);
st[0] = true;
dis[0] = 0;
while (!q.isEmpty()) {
int t = q.poll();
st[t] = false;
for (int i = h[t]; i != 0; i = ne[i]) {
int j = e[i];
if (dis[j] < dis[t] + w[i]) {
dis[j] = dis[t] + w[i];
if (!st[j]) {
q.add(j);
st[j] = true;
}
}
}
}
}
static int n, m, N = 50010, M = 150010, count = 1;
static int[] h = new int[N];
static int[] e = new int[M];
static int[] ne = new int[M];
static int[] w = new int[M];
static int[] dis = new int[N];
static boolean[] st = new boolean[N];
static ArrayDeque<Integer> q = new ArrayDeque<Integer>();
static void add(int a, int b, int c) {
e[count] = b;
w[count] = c;
ne[count] = h[a];
h[a] = count++;
}
}
#差分约束
###功能
````
1:求不等式的可行解,
源点满足,从源点出发,一定可以走到所有的边
2:求最大值或最小值
````
###方法
````
形如
Xi<=Xj+Ck
的一堆不等式,可以求出可行解
其中i,j为自变量,c为常数
{
x1<=x2+1
x2<=x3+2
x3<=x1-2
}
对于
Xi<=Xj+Ck
我们类比三角不等式
看做j->i连一条权值为c的边
有dis[i]<=dis[j]+c
因为从j走到i,如果反之则不能走这条边
````
###步骤
````
1:Xi<=Xj+Ck
将不等式格式化,转化为j走到i长度为Ck的边
不一定走到所有点,但一定要能走到所有边,限制的是边
孤立的点也就是没有限制,
2:找到虚拟源点,使得虚拟源点可以到达所有的边
3:从源点求单源最短路
如果存在负环,说明不等式组无解!
如:X1->X2->X1 权值之和小于0
X2<=X1+C1
X1<=X2+C2
放缩X2<=X2+C2+C1
显然矛盾
如果没有负环,则dis[i]就是可行解
做一个对偶Xk>=Xi-Ck
i->连一条-Ck的边
dis[j]>=dis[i]+Ck求最长路
如果是求的最小值,求最长路,
如果求的是最长值,应该求最短路
应该有绝对关系,比如Xi>=0
转化Xi<=c c为常数,这类的不等式
建立超级源点为X0到i长度是0的边
Xi<=C转化为Xi<=X0+C
构成不等式链对应路径,Xi<=Xj+C1<=Xk+C2+C1<=...<=X0+C1+C2...
Xi的最大值为一个所有上界的最小值等价于求0到i路径的最小值
package graph.差分约束;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Scanner;
/**
* https://blog.csdn.net/weixin_43872728/article/details/105941583
* 当排队等候喂食时,奶牛喜欢和它们的朋友站得靠近些。
* 农夫约翰有 N 头奶牛,编号从 1 到 N,沿一条直线站着等候喂食。
* 奶牛排在队伍中的顺序和它们的编号是相同的。
* 因为奶牛相当苗条,所以可能有两头或者更多奶牛站在同一位置上。
* 如果我们想象奶牛是站在一条数轴上的话,允许有两头或更多奶牛拥有相同的横坐标。
* 一些奶牛相互间存有好感,它们希望两者之间的距离不超过一个给定的数 L。
* 另一方面,一些奶牛相互间非常反感,它们希望两者间的距离不小于一个给定的数 D。
* 给出 ML 条关于两头奶牛间有好感的描述,再给出 MD 条关于两头奶牛间存有反感的描述。
* 你的工作是:如果不存在满足要求的方案,输出-1;如果 1 号奶牛和 N 号奶牛间的距离可以任意大,输出-2;否则,计算出在满足所有要求的情况下,1 号奶牛和 N 号奶牛间可能的最大距离。
* 输入格式
* 第一行包含三个整数 N,ML,MD。
* 接下来 ML 行,每行包含三个正整数 A,B,L,表示奶牛 A 和奶牛 B 至多相隔 L 的距离。
* 再接下来 MD 行,每行包含三个正整数 A,B,D,表示奶牛 A 和奶牛 B 至少相隔 D 的距离。
* 输出格式
* 输出一个整数,如果不存在满足要求的方案,输出-1;如果 1 号奶牛和 N 号奶牛间的距离可以任意大,输出-2;否则,输出在满足所有要求的情况下,1 号奶牛和 N 号奶牛间可能的最大距离。
* 数据范围
* 2≤N≤1000,
* 1≤ML,MD≤104,
* 1≤L,D≤106
* 思路
* 不等式,并且使所有点联通,i<=i+1+0
* 加入i+1向i的边
*/
public class 排队布局 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
int m1 = sc.nextInt();
int m2 = sc.nextInt();
for (int i = 1; i < n; i++) {
add(i + 1, i, 0);
}
int a, b, c, t;
while (m1-- != 0) {
a = sc.nextInt();
b = sc.nextInt();
c = sc.nextInt();
if (b < a) {
t = b;
b = a;
a = t;
}
add(a, b, c);
}
while (m2-- != 0) {
a = sc.nextInt();
b = sc.nextInt();
c = sc.nextInt();
if (b < a) {
t = b;
b = a;
a = t;
}
add(b, a, -c);
}
if (!spfa(n)) System.out.println(-1);
else {
spfa(1);
if (dis[n] == Integer.MAX_VALUE / 2) System.out.println(-2);
else System.out.println(dis[n]);
}
}
static boolean spfa(int size) {
Arrays.fill(dis, Integer.MAX_VALUE / 2);
Arrays.fill(st, false);
Arrays.fill(cnt, 0);
q.clear();
for (int i = 1; i <= size; i++) {
dis[i] = 0;
q.add(i);
st[i] = true;
}
while (!q.isEmpty()) {
int t = q.poll();
st[t] = false;
for (int i = h[t]; i != 0; i = ne[i]) {
int j = e[i];
if (dis[j] > dis[t] + w[i]) {
dis[j] = dis[t] + w[i];
cnt[j] = cnt[t] + 1;
if (cnt[j] >= n) return false;
if (!st[j]) {
q.add(j);
st[j] = true;
}
}
}
}
return true;
}
static int n, m, N = 1010, M = 21010, count = 1;
static int[] h = new int[N];
static int[] e = new int[M];
static int[] ne = new int[M];
static int[] w = new int[M];
static int[] dis = new int[N];
static int[] cnt = new int[N];
static boolean[] st = new boolean[N];
static ArrayDeque<Integer> q = new ArrayDeque<Integer>();
static void add(int a, int b, int c) {
e[count] = b;
w[count] = c;
ne[count] = h[a];
h[a] = count++;
}
}
package graph.差分约束;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Scanner;
/**
* https://blog.csdn.net/qq_42279796/article/details/105072757
* 幼儿园里有 N 个小朋友,老师现在想要给这些小朋友们分配糖果,
* 要求每个小朋友都要分到糖果.
* 但是小朋友们也有嫉妒心,总是会提出一些要求,
* 比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候,
* 老师需要满足小朋友们的 K 个要求。
* 幼儿园的糖果总是有限的,老师想知道他至少需要准备多少个糖果,
* 才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。
* 输入格式
* 输入的第一行是两个整数 N,K。
* 接下来 K 行,表示分配糖果时需要满足的关系,每行 3 个数字 X,A,B。
* 如果 X=1.表示第 A 个小朋友分到的糖果必须和第 B 个小朋友分到的糖果一样多。
* 如果 X=2,表示第 A 个小朋友分到的糖果必须少于第 B 个小朋友分到的糖果。
* 如果 X=3,表示第 A 个小朋友分到的糖果必须不少于第 B 个小朋友分到的糖果。
* 如果 X=4,表示第 A 个小朋友分到的糖果必须多于第 B 个小朋友分到的糖果。
* 如果 X=5,表示第 A 个小朋友分到的糖果必须不多于第 B 个小朋友分到的糖果。
* 小朋友编号从 1 到 N。
* 输出格式
* 输出一行,表示老师至少需要准备的糖果数,如果不能满足小朋友们的所有要求,就输出 −1。
* 数据范围
* 1≤N<105,1≤X≤5,1≤A,B≤N1≤N<10^5,1≤X≤5,1≤A,B≤N
* 输入样例:
* 5 7
* 1 1 2
* 2 3 2
* 4 4 1
* 3 4 5
* 5 4 5
* 2 3 5
* 4 5 1
* 输出样例:
* 11
* 思路:
* 因为我们要求的是最小值且约束条件可转换为若干不等式,所以我们可以用差分约束来做,求最长路,不等式为大于号。
* 将题目转换成一堆不等式
* x=1=》A>=B&&B>=A
* x=2=》B>=A+1
* x=3=》A>=B
* x=4=》A>=B+1
* x=5=》B>=A
* 设一个超级原点x0,x0=0;
* 因为每个人至少有一个糖果,所以xi>=x0+1。
* 本题可能出现无解的情况,即A>B+1&&B>A+1,所以需要判断是否存在环路,此时使用SPFA时应将queue改为stack,能提高速度。
* 因为N较大,所以建图时不要使用vector(会超时),用前向星就行。
*/
public class 糖果 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
int x, a, b;
while (m-- != 0) {
x = sc.nextInt();
a = sc.nextInt();
b = sc.nextInt();
if (x == 1) {
add(b, a, 0);
add(a, b, 0);
} else if (x == 2) {
add(a, b, 1);
} else if (x == 3) {
add(b, a, 0);
} else if (x == 4) {
add(b, a, 1);
} else add(a, b, 0);
}
for (int i = 1; i <= n; i++) {
add(0, i, 1);
}
if (!spfa()) System.out.println("-1");
else {
long res = 0;
for (int i = 1; i <= n; i++) {
res += dis[i];
}
System.out.println(res);
}
}
private static boolean spfa() {//求负环改成栈!!!
Arrays.fill(dis, finf);
dis[0] = 0;
q.clear();
q.add(0);
st[0] = true;
while (!q.isEmpty()) {
int t = q.pollLast();
st[t] = false;
for (int i = h[t]; i != 0; i = ne[i]) {
int j = e[i];
if (dis[j] < dis[t] + w[i]) {//最长路!
dis[j] = dis[t] + w[i];
cnt[j] = cnt[t] + 1;
if (cnt[j] >= n + 1) return false;//有负环
if (!st[j]) {
q.add(j);
st[j] = true;
}
}
}
}
return true;
}
static int finf = Integer.MIN_VALUE / 2;
static int n, m, N = 100010, M = 300010, count = 1;
static int[] h = new int[N];
static int[] e = new int[M];
static int[] ne = new int[M];
static int[] w = new int[M];
static int[] dis = new int[N];
static int[] cnt = new int[N];
static boolean[] st = new boolean[N];
static ArrayDeque<Integer> q = new ArrayDeque<Integer>();
static void add(int a, int b, int c) {
e[count] = b;
w[count] = c;
ne[count] = h[a];
h[a] = count++;
}
}
package graph.差分约束;
/**
*
*/
public class 雇佣收银员 {
public static void main(String[] args) {
}
}
...@@ -4,7 +4,7 @@ import java.util.PriorityQueue; ...@@ -4,7 +4,7 @@ import java.util.PriorityQueue;
import java.util.Scanner; import java.util.Scanner;
/** /**
* https://blog.csdn.net/weixin_43872728/article/details/105852223 * https://blog.csdn.net/qq_30277239/article/details/107898613
* 求最小生成森林 * 求最小生成森林
* 求每一个联通块的,最小生成树 * 求每一个联通块的,最小生成树
* 使用Kruskal算法最好写,即使算法没有进行完,那么算法也是正确的 * 使用Kruskal算法最好写,即使算法没有进行完,那么算法也是正确的
......
...@@ -4,7 +4,7 @@ import java.util.PriorityQueue; ...@@ -4,7 +4,7 @@ import java.util.PriorityQueue;
import java.util.Scanner; import java.util.Scanner;
/** /**
* https://www.acwing.com/activity/content/code/content/308366/ * https://blog.csdn.net/qq_30277239/article/details/107899568
* 求出无向图最小生成树中,最长边权的最小值 * 求出无向图最小生成树中,最长边权的最小值
* 最小生成树的权值最大的边,对应Kruskal就是最后取到的那条边 * 最小生成树的权值最大的边,对应Kruskal就是最后取到的那条边
*/ */
......
...@@ -3,7 +3,7 @@ package graph.最小生成树; ...@@ -3,7 +3,7 @@ package graph.最小生成树;
import java.util.Scanner; import java.util.Scanner;
/** /**
* https://blog.csdn.net/qq_42279796/article/details/103091446 * https://blog.csdn.net/qq_30277239/article/details/107900698
* 依然Kruskal * 依然Kruskal
* 先选一些,再用Kruskal * 先选一些,再用Kruskal
* 把二维坐标转化为一维坐标 * 把二维坐标转化为一维坐标
......
...@@ -11,6 +11,13 @@ import java.util.Scanner; ...@@ -11,6 +11,13 @@ import java.util.Scanner;
* 具有单调性,随着d的增加,k单调减少 * 具有单调性,随着d的增加,k单调减少
* 最终Kruskal本质上在求连通性 * 最终Kruskal本质上在求连通性
* 当前循环完第i条边,求出由前i条边所构成的所有连通块 * 当前循环完第i条边,求出由前i条边所构成的所有连通块
* 输入样例:
* 3 2
* 10 10
* 10 0
* 30 0
* 输出样例:
* 10.00
*/ */
public class 北极通讯网络 { public class 北极通讯网络 {
public static void main(String[] args) { public static void main(String[] args) {
...@@ -28,11 +35,12 @@ public class 北极通讯网络 { ...@@ -28,11 +35,12 @@ public class 北极通讯网络 {
for (int j = 0; j < i; j++) { for (int j = 0; j < i; j++) {
q.add(new node(i, j, getdist(list.get(i), list.get(j)))); q.add(new node(i, j, getdist(list.get(i), list.get(j))));
} }
} }//预处理出所有的连接边,
int cnt = n; int cnt = n;
double res = 0; double res = 0;
//枚举所有的,
while (!q.isEmpty()) { while (!q.isEmpty()) {
if (cnt <= k) break; if (cnt <= k) break;//剩下的村庄可以通过卫星连接,不需要权值边,蛮优秀的想法
node p = q.poll(); node p = q.poll();
int a = fin(p.x), b = fin(p.y); int a = fin(p.x), b = fin(p.y);
double w = p.w; double w = p.w;
......
...@@ -8,7 +8,7 @@ import java.util.Scanner; ...@@ -8,7 +8,7 @@ import java.util.Scanner;
* 本来不是最小生成树问题 * 本来不是最小生成树问题
* 把在该节点新建一条边,看做向虚拟源点(0号点)连一条边 * 把在该节点新建一条边,看做向虚拟源点(0号点)连一条边
* 虚拟源点,是个非常重要的技巧,这样就变成了最小生成树问题 * 虚拟源点,是个非常重要的技巧,这样就变成了最小生成树问题
* * 图论问题,一定要先想到虚拟源点技巧
*/ */
public class 新的开始 { public class 新的开始 {
public static void main(String[] args) { public static void main(String[] args) {
......
package graph.最小生成树拓展;
/**
* https://blog.csdn.net/qq_41661919/article/details/86565228
* Input
* 5 6
* 1 2 1
* 1 3 2
* 2 4 3
* 3 5 4
* 3 4 3
* 4 5 6
* Output
* 11
* 先求出最小生成树,并建树图,之后遍历所有非树边,用树上倍增
* 求LCA的方法求出非树边两节点之间树边中的最大边和次大边,再将非
* 树边权值与最大值比较,如果最大边<非树边(或者不等于,不等于一
* 定<,要不然最小生成树就不是最小了)权值,用非树边替换最大边,否
* 则(等于关系)用非树边替换次大边,最后从所有候选答案中选择最小值
* 即次小生成树权值。
*/
public class 次小生成树 {
public static void main(String[] args) {
}
}
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
定义:给定一个带权的图,把图的所有生成树按权值从小到大排序 定义:给定一个带权的图,把图的所有生成树按权值从小到大排序
第二小的称为次小生成树 第二小的称为次小生成树
则显然第一种定义,最小生成树和次小生成树的权值和相等 则显然第一种定义,最小生成树和次小生成树的权值和可能相等
第二种定义:次小生成树严格大于最小生成树的权值和 第二种定义:次小生成树严格大于最小生成树的权值和
次小生成树是权值和大于最小生成树的那个最小的那个生成树 次小生成树是权值和大于最小生成树的那个最小的那个生成树
...@@ -13,5 +13,9 @@ ...@@ -13,5 +13,9 @@
方法1:求最小生成树,再枚举删去最小生成树中的边求解 方法1:求最小生成树,再枚举删去最小生成树中的边求解
O(mlog m)+O(nm) 无法求严格次小生成树 O(mlog m)+O(nm) 无法求严格次小生成树
方法2:先求最小生成树,依次枚举非树边, 方法2:先求最小生成树,依次枚举非树边,可以求出严格最小生成树
然后将该边加入树,同时从树中去掉一条边,使得最终的图仍然是树,则一定可以求出次小生成树 然后将该边加入树,同时从树中去掉一条边,使得最终的图仍然是树,则一定可以求出次小生成树
预处理,以任意节点为根,(BFS/DFS)枚举到任何点的最大边权O(n^2), 最终O(n^2+m+n^2+mlogn)
sum+w新-w树
LCA预处理O(mlogn+m+n^2+mlogn)
存在一棵次小生成树和最小生成树相差一条边
package graph.最小生成树拓展;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
/**
* https://blog.csdn.net/qq_41661919/article/details/86565228
* https://blog.csdn.net/qq_44828887/article/details/107305636
* 给定一张 N 个点 M 条边的无向图,求无向图的严格次小生成树。
* 设最小生成树的边权之和为sum,严格次小生成树就是指边权之和大于sum的生成树中最小的一个。
* 输入格式
* 第一行包含两个整数N和M。
* 接下来M行,每行包含三个整数x,y,z,表示点x和点y之前存在一条边,边的权值为z。
* 输出格式
* 包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
* 数据范围
* N≤105,M≤3∗105
* 思路
* lca次小生成树。倍增找树上路径最大边即可。
*/
public class 牛奶运输 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
int a, b, w;
for (int i = 0; i < m; i++) {
a = sc.nextInt();
b = sc.nextInt();
w = sc.nextInt();
edge.add(new node(a, b, w));
}
Collections.sort(edge);
for (int i = 1; i <= n; i++) {
p[i] = i;
}
long sum = 0;
for (int i = 0; i < m; i++) {
a = find(edge.get(i).a);
b = find(edge.get(i).b);
w = edge.get(i).w;
if (a != b) {
p[a] = b;
sum += w;
add(a, b, w);
add(b, a, w);
edge.get(i).isShu = true;
}
}
for (int i = 1; i <= n; i++) {
dfs(i, -1, 0, dis[i]);
}
long res = (long) 1e18;
for (int i = 0; i < m; i++) {
if (!edge.get(i).isShu) {
a = edge.get(i).a;
b = edge.get(i).b;
w = edge.get(i).w;
if (w > dis[a][b]) {
res = Math.min(res, sum + w - dis[a][b]);
}
}
}
System.out.println(res);
}
static void dfs(int u, int fa, int maxd, int[] d) {
d[u] = maxd;
for (int i = h[u]; i != 0; i = ne[i]) {
int j = e[i];
if (j != fa) {
dfs(j, u, Math.max(maxd, w[i]), d);
}
}
}
static void add(int a, int b, int c) {
e[cnt] = b;
w[cnt] = c;
ne[cnt] = h[a];
h[a] = cnt++;
}
static int find(int a) {
if (p[a] != a) return p[a] = find(p[a]);
return a;
}
static class node implements Comparable<node> {
int a, b, w;
boolean isShu;
public node(int a, int b, int w) {
this.a = a;
this.b = b;
this.w = w;
}
@Override
public int compareTo(node node) {
return w - node.w;
}
}
static int n, m, N = 510, cnt = 1, M = 10010;
static ArrayList<node> edge = new ArrayList<node>();
static int[] p = new int[N];//并查集
static int[][] dis = new int[N][N];
static int[] h = new int[N];
static int[] e = new int[M];
static int[] w = new int[M];
static int[] ne = new int[M];
}
package graph.最小生成树拓展; package graph.最小生成树拓展;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Scanner; import java.util.Scanner;
...@@ -9,6 +10,8 @@ import java.util.Scanner; ...@@ -9,6 +10,8 @@ import java.util.Scanner;
* 给定一个N个节点的树, * 给定一个N个节点的树,
* 把这棵树扩充为完全图,并满足图的唯一最小生成树仍然是这棵树 * 把这棵树扩充为完全图,并满足图的唯一最小生成树仍然是这棵树
* 连接两个集合的新边, * 连接两个集合的新边,
* 把最小生成树,扩充,则新加的边必须严格大于最小生成树的边,
* 都取边权w+1最小的那个满足要求
*/ */
public class 走廊泼水节 { public class 走廊泼水节 {
public static void main(String[] args) { public static void main(String[] args) {
...@@ -32,8 +35,9 @@ public class 走廊泼水节 { ...@@ -32,8 +35,9 @@ public class 走廊泼水节 {
node c = q.get(i); node c = q.get(i);
int a = find(c.x), b = find(c.y); int a = find(c.x), b = find(c.y);
if (a != b) { if (a != b) {
res += (size[a] * size[b] - 1) * (c.w + 1); res += (size[a] * size[b] - 1) * (c.w + 1);//新边都取w+1
size[b] += size[a]; System.out.println(Arrays.toString(size));
size[b] += size[a];//合并两个集合
par[a] = b; par[a] = b;
} }
} }
......
```
对于一个有向图,连通分量:对于分量中的任意两点u,v
必然可以从u走到v,且从v走到u
强联通分量:极大联通分量
有向图->缩点,有向无环图(DAG拓扑图)
缩点,将所有连通分量缩成一个点
按照dfs序来求
分为四类:树枝边(x,y)
前向边(x,y)
后向边
横叉边
SCC强连通分量
...@@ -77,11 +77,7 @@ public class 洛谷负环 { ...@@ -77,11 +77,7 @@ public class 洛谷负环 {
if (count[t] >= n) return true; if (count[t] >= n) return true;
if (!vis[t]) { if (!vis[t]) {
vis[t] = true; vis[t] = true;
if (!q.isEmpty() && dis[t] < dis[q.peekFirst()]) { q.add(t);
q.addFirst(t);
} else {
q.add(t);
}
} }
} }
} }
...@@ -89,15 +85,15 @@ public class 洛谷负环 { ...@@ -89,15 +85,15 @@ public class 洛谷负环 {
return false; return false;
} }
static boolean[] vis = new boolean[10005]; static boolean[] vis = new boolean[2005];
static ArrayDeque<Integer> q = new ArrayDeque<Integer>(); static ArrayDeque<Integer> q = new ArrayDeque<Integer>();
static int[] count = new int[10005]; static int[] count = new int[2005];
static int[] dis = new int[10005]; static int[] dis = new int[2005];
static int t, n, m, cnt = 1; static int t, n, m, cnt = 1;
static int[] he = new int[10005]; static int[] he = new int[2005];
static int[] ne = new int[10005]; static int[] ne = new int[6005];
static int[] e = new int[10005]; static int[] e = new int[6005];
static int[] w = new int[10005]; static int[] w = new int[6005];
static void add(int a, int b, int c) { static void add(int a, int b, int c) {
e[cnt] = b; e[cnt] = b;
......
package graph.负环;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Scanner;
/**
* https://blog.csdn.net/qq_44828887/article/details/107271849
* 我们有 n 个字符串,每个字符串都是由 a∼z 的小写英文字母组成的。
* 如果字符串 A 的结尾两个字符刚好与字符串 B 的开头两个字符相匹配,
* 那么我们称 A 与 B 能够相连(注意:A 能与 B 相连不代表 B 能与 A 相连)。
* 我们希望从给定的字符串中找出一些,
* 使得它们首尾相连形成一个环串(一个串首尾相连也算),
* 我们想要使这个环串的平均长度最大。
* 如下例:
*/
public class 单词环 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
char[] str;
while (sc.hasNext()) {
n = sc.nextInt();
Arrays.fill(h, 0);
cnt = 1;
for (int i = 0; i < n; i++) {
str = sc.next().toCharArray();
int len = str.length;
if (len >= 2) {
int left = (str[0] - 'a') * 26 + str[1] - 'a';//看做一个26进制数
int right = (str[len - 2] - 'a') * 26 + str[len - 1] - 'a';
add(left, right, len);
}
}
if (!check(0)) System.out.println("无解");
else {
double l = 0, r = 1000;
while (r - l > 1e-5) {
double mid = (l + r) / 2;
if (check(mid)) l = mid;
else r = mid;
}
System.out.println(r);
}
}
}
private static boolean check(double mid) {
Arrays.fill(st, false);
Arrays.fill(count, 0);
ArrayDeque<Integer> q = new ArrayDeque<Integer>();
for (int i = 0; i < 676; i++) {
q.add(i);
st[i] = true;
}
int cn = 0;
while (!q.isEmpty()) {
int t = q.pollLast();//换成栈
st[t] = true;
for (int i = h[t]; i != 0; i = ne[i]) {
int j = e[i];
if (dis[j] < dis[t] + w[i] - mid) {
dis[j] = dis[t] + w[i] - mid;
count[j] = count[t] + 1;
// if (count[j] >= N) return true;
if (++cn > 30000) return true;//经验trick,或者换成把spfa的队列换成栈
if (!st[j]) {
q.add(j);
st[j] = true;
}
}
}
}
return false;
}
static int n, cnt = 1, N = 700, M = 100100;
static int[] h = new int[N];
static int[] e = new int[M];
static int[] w = new int[M];
static int[] ne = new int[M];
static double[] dis = new double[N];
static int[] count = new int[N];
static boolean[] st = new boolean[N];
static void add(int a, int b, int c) {
e[cnt] = b;
w[cnt] = c;
ne[cnt] = h[a];
h[a] = cnt++;
}
}
...@@ -5,7 +5,33 @@ import java.util.Arrays; ...@@ -5,7 +5,33 @@ import java.util.Arrays;
import java.util.Scanner; import java.util.Scanner;
/** /**
* https://www.cnblogs.com/ctyakwf/p/12840842.html * https://blog.csdn.net/qq_44828887/article/details/107271471
* 农夫约翰在巡视他的众多农场时,发现了很多令人惊叹的虫洞。
* 虫洞非常奇特,它可以看作是一条 单向 路径,通过它可以使你回到过去的某个时刻
* (相对于你进入虫洞之前)。
* 农夫约翰的每个农场中包含N片田地,M条路径(双向)以及W个虫洞。
* 现在农夫约翰希望能够从农场中的某片田地出发,经过一些路径和虫洞回到过去,
* 并在他的出发时刻之前赶到他的出发地。
* 他希望能够看到出发之前的自己。
* 请你判断一下约翰能否做到这一点。
* 下面我们将给你提供约翰拥有的农场数量F,以及每个农场的完整信息。
* 已知走过任何一条路径所花费的时间都不超过10000秒,
* 任何虫洞将他带回的时间都不会超过10000秒。
* 输入格式
* 第一行包含整数F,表示约翰共有F个农场。
* 对于每个农场,第一行包含三个整数N,M,W。
* 接下来M行,每行包含三个整数S,E,T,表示田地S和E之间存在一条路径,经过这条路径所花的时间为T。
* 再接下来W行,每行包含三个整数S,E,T,表示存在一条从田地S走到田地E的虫洞,走过这条虫洞,可以回到T秒之间。
* 输出格式
* 输出共F行,每行输出一个结果。
* 如果约翰能够在出发时刻之前回到出发地,则输出“YES”,否则输出“NO”。
* 数据范围
* 1≤F≤5
* 1≤N≤500,
* 1≤M≤2500,
* 1≤W≤200,
* 1≤T≤10000,
* 1≤S,E≤N
* 2 * 2
* 3 3 1 * 3 3 1
* 1 2 2 * 1 2 2
...@@ -16,6 +42,13 @@ import java.util.Scanner; ...@@ -16,6 +42,13 @@ import java.util.Scanner;
* 1 2 3 * 1 2 3
* 2 3 4 * 2 3 4
* 3 1 8 * 3 1 8
* out
* NO
* YES
* 由于虫洞是回到之前 所以可以看做是田地为点的负权值边。
* 田地之间是双向边。
* 只要检测到图中存在负环,那么就可以无限穿越到之前的时间,
* 而由于田地点之间是双向边,所以肯定能到达任意一个田地(也包括起点)。所以本题目就是判断图中是否存在负环。
*/ */
public class 虫洞 { public class 虫洞 {
public static void main(String[] args) { public static void main(String[] args) {
......
package graph.负环;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Scanner;
/**
* https://blog.csdn.net/qq_44828887/article/details/107271543
* https://blog.csdn.net/tomjobs/article/details/105252069
* 给定一张L个点、P条边的有向图,每个点都有一个权值f[i],每条边都有一个权值t[i]。
* 求图中的一个环,使“环上各点的权值之和”除以“环上各边的权值之和”最大。
* 输出这个最大值。
* 注意:数据保证至少存在一个环。
* 输入格式
* 第一行包含两个整数L和P。
* 接下来L行每行一个整数,表示f[i]。
* 再接下来P行,每行三个整数a,b,t[i],表示点a和b之间存在一条边,边的权值为t[i]。
* 输出格式
* 输出一个数表示结果,保留两位小数。
* 数据范围
* 2≤L≤1000,
* 2≤P≤5000,
* 1≤f[i],t[i]≤1000
* 思路
* 01分数规划。
* 输入样例:
* 5 7
* 30
* 10
* 10
* 5
* 10
* 1 2 3
* 2 3 2
* 3 4 5
* 3 5 2
* 4 5 5
* 5 1 3
* 5 2 2
* 输出样例:
* 6.00
* 每个点都有权值,每条边都有权值,求一个环,
* 使得每个点的权值和除以每条边的权值和最大
* 01分数规划,∑fi/∑ti最大,可以二分找正环
*/
public class 观光奶牛 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
for (int i = 1; i <= n; i++) {
wf[i] = sc.nextInt();
}
int a, b, c;
while (m-- != 0) {
a = sc.nextInt();
b = sc.nextInt();
c = sc.nextInt();
add(a, b, c);
}
double l = 0, r = 1e6;
while (r - l > 1e-4) {//保留两位小数,多加2位,
double mid = (l + r) / 2;
if (check(mid)) l = mid;
else r = mid;
}
System.out.printf("%.2f", l);
}
private static boolean check(double mid) {
Arrays.fill(st, false);
Arrays.fill(count, 0);
Arrays.fill(dis, 0);
ArrayDeque<Integer> q = new ArrayDeque<Integer>();
for (int i = 1; i <= n; i++) {
q.add(i);
st[i] = true;
}
while (!q.isEmpty()) {
int t = q.poll();
st[t] = false;
for (int i = h[t]; i != 0; i = ne[i]) {
int j = e[i];
if (dis[j] < dis[t] + wf[t] - mid * wt[i]) {
dis[j] = dis[t] + wf[t] - mid * wt[i];
count[j] = count[t] + 1;
if (count[j] >= n) return true;
if (!st[j]) {
q.add(j);
st[j] = true;
}
}
}
}
return false;
}
private static void add(int a, int b, int c) {
e[cnt] = b;
wt[cnt] = c;
ne[cnt] = h[a];
h[a] = cnt++;
}
static int n, m, N = 1010, M = 10100, cnt = 1;
static int[] h = new int[N];
static int[] e = new int[M];
static int[] wf = new int[N];
static int[] ne = new int[M];
static int[] wt = new int[M];
static double[] dis = new double[N];
static int[] count = new int[N];
static boolean[] st = new boolean[N];
}
...@@ -14,3 +14,13 @@ ...@@ -14,3 +14,13 @@
如果某个点的最短路所包含的边数大于等于n, 如果某个点的最短路所包含的边数大于等于n,
则也说明存在环 则也说明存在环
O(n)推荐 O(n)推荐
考虑1~n组成负环,第一种做法是O(n^2) n(n-1)+1次
初始化,queue加入所有节点,看做超级源点连接每个点权值都为0
dis不用初始化
如果有负环,dis赋值为有限值,一直减结果必然会变成负无穷
只有BellmanFord 和Floyd才有 0x3f3f3f3f/2
spfa判断环,把队列换成栈跑的更快
\ No newline at end of file
package 数学;
import java.util.Scanner;
/**
* https://blog.csdn.net/njuptACMcxk/article/details/107328091
* BSNY 在学等差数列和等比数列,当已知前三项时,就可以知道是等差数列还是等比数列。
* 现在给你 整数 序列的前三项,这个序列要么是等差序列,要么是等比序列,你能求出第 k 项的值吗。
* 如果第 k 项的值太大,对其取模 200907。
* 输入格式
* 第一行一个整数 T,表示有 T 组测试数据;
* 对于每组测试数据,输入前三项 a,b,c,然后输入 k。
* 输出格式
* 对于每组数据,输出第 k 项取模 200907 的值。
* 数据范围
* 1≤T≤100,1≤a≤b≤c≤109,1≤k≤1091≤T≤100,1≤a≤b≤c≤10^9
* ,1≤k≤10^9
* 输入样例:
* 2
* 1 2 3 5
* 1 2 4 5
* 输出样例:
* 5
* 16
*/
public class 序列的第k个数 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int a, b, c, d, k;
d = sc.nextInt();
for (int i = 0; i < d; i++) {
a = sc.nextInt();
b = sc.nextInt();
c = sc.nextInt();
k = sc.nextInt();
if (a + c == 2 * b) {
System.out.println((a + (k - 1) * (b - a)) % mod);
} else {
System.out.println((a * qmi((b / a), k-1)) % mod);
}
}
}
static long qmi(long a, long b) {
long res = 1;
while (b != 0) {
if ((b & 1) == 1) {
res = (res * a) % mod;
}
a = (a * a) % mod;
b >>= 1;
}
return res;
}
static int mod = 200907;
}
package 数学;
import java.util.Scanner;
/**
* https://www.acwing.com/solution/acwing/content/12579/
* https://www.hzxueyan.com/archives/85/
* 监狱有连续编号为 11 到 nn 的 nn 个房间,每个房间关押一个犯人。
* 有 mm 种宗教,每个犯人可能信仰其中一种。
* 如果相邻房间的犯人信仰的宗教相同,就可能发生越狱。
* 求有多少种状态可能发生越狱。
* 输入
* 共一行,包含两个整数 mm 和 nn。
* 输出
* 可能越狱的状态数,对 100003取余。
* 数据范围
* 输入样例
* 2 3
* 输出样例
* 6
* 样例解释
* 所有可能的 66 种状态为:(000)(001)(011)(100)(110)(111)(000)(001)(011)(100)(110)(111)。
*/
public class 越狱 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
m = sc.nextInt();
n = sc.nextInt();
System.out.println((qmi(m, n) - m * qmi(m - 1, n - 1)) % mod);
}
static long qmi(long a, long b) {
long res = 1;
while (b != 0) {
if ((b & 1) == 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
static int n, m, mod = 100003;
}
package 数学; package 数学;
import java.util.Scanner;
/** /**
* * https://blog.csdn.net/aoying6521/article/details/101785682
* https://www.acwing.com/solution/acwing/content/982/
* https://www.hzxueyan.com/archives/83/
* 给定整数 N ,试把阶乘 N!分解质因数,按照算术基本定理的形式输出分解结果中的pi和 ci
* 即可。
* 输入样例
* 5
* 输出样例
* 2 3
* 3 1
* 5 1
*/ */
public class 阶乘分解 { public class 阶乘分解 {
public static void main(String[] args) { public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
init(n);
for (int i = 0; i < cnt; i++) {
int t = p[i];
int s = 0;
for (int j = n; j != 0; j /= t) {
s += j / t;
}
System.out.println(t + " " + s);
}
}
static void init(int n) {
for (int i = 2; i <= n; i++) {
if (!st[i]) p[cnt++] = i;
for (int j = 0; p[j] <= n / i; j++) {
st[p[j] * i] = true;
if (i % p[j] == 0) break;
}
}
} }
static int n, cnt = 0;
static boolean[] st = new boolean[(int) (1e6 + 10)];
static int[] p = new int[(int) (1e6 + 10)];
} }
package 线段树; package 树状数组;
import java.io.*; import java.io.*;
import java.util.StringTokenizer; import java.util.StringTokenizer;
......
###树状数组
````
1.快速求前缀和 O(log n)
2.修改某一个数 O(log n)
对比前缀和
修改单点修改O(n)
查询O(1)
基于二进制的想法解决问题
x=2^ik+2^i(k-1)+...+2^i
ik>=i(k-1)>=...>=i
1~x表示
package 线段树; package 树状数组;
import java.io.*; import java.io.*;
import java.util.StringTokenizer; import java.util.StringTokenizer;
...@@ -38,7 +38,7 @@ public class 树状数组区间修改 { ...@@ -38,7 +38,7 @@ public class 树状数组区间修改 {
n = nextInt(); n = nextInt();
m = nextInt(); m = nextInt();
for (int i = 1; i <= n; i++) { for (int i = 1; i <= n; i++) {
cha[i] = nextInt(); a[i] = nextInt();
} }
int x, y, z, t; int x, y, z, t;
while (m-- != 0) { while (m-- != 0) {
...@@ -51,7 +51,7 @@ public class 树状数组区间修改 { ...@@ -51,7 +51,7 @@ public class 树状数组区间修改 {
add(y + 1, -z); add(y + 1, -z);
} else if (t == 2) { } else if (t == 2) {
x = nextInt(); x = nextInt();
bw.write((ask(x) + cha[x]) + "\n"); bw.write((ask(x) + a[x]) + "\n");
} }
} }
bw.flush(); bw.flush();
...@@ -59,7 +59,7 @@ public class 树状数组区间修改 { ...@@ -59,7 +59,7 @@ public class 树状数组区间修改 {
static int maxn = 500001, n, m; static int maxn = 500001, n, m;
static long[] tree = new long[maxn]; static long[] tree = new long[maxn];
static long[] cha = new long[maxn]; static long[] a = new long[maxn];
//差分数组 //差分数组
static void add(int s, int value) { static void add(int s, int value) {
......
package 线段树; package 树状数组;
import java.util.Arrays; import java.util.Arrays;
import java.util.Scanner; import java.util.Scanner;
/** /**
* 题目https://www.acwing.com/problem/content/description/243/ * 题目
* https://www.acwing.com/problem/content/description/243/
* https://www.acwing.com/solution/acwing/content/1008/ * https://www.acwing.com/solution/acwing/content/1008/
* 巧妙想法,也可以求逆序数!!!
* 建树状数组O(nlogn)
* 天才想法太巧妙啊
*/ */
public class 楼兰图腾 { public class 楼兰图腾 {
public static void main(String[] args) { public static void main(String[] args) {
...@@ -13,18 +17,40 @@ public class 楼兰图腾 { ...@@ -13,18 +17,40 @@ public class 楼兰图腾 {
n = sc.nextInt(); n = sc.nextInt();
for (int i = 1; i <= n; i++) { for (int i = 1; i <= n; i++) {
a[i] = sc.nextInt(); a[i] = sc.nextInt();
max_value = Math.max(a[i], max_value);//记录数组最大值 // max_value = Math.max(a[i], max_value);//记录数组最大值
} }
res1(); /**
System.out.println(sumans()); * 正序循环,巧妙想法,也可以求逆序数
* 这里great记录左边有多少个比它大的,
* lower记录左边有多少比它小的
*/
for (int i = 1; i <= n; i++) {
int y=a[i];
great[i] = ask(n) - ask(y);
lower[i] = ask(y-1);
add(y, 1);
}
Arrays.fill(c, 0);
/**
* 倒序循环这里great记录右边有多少个比它大的,
* lower记录右边有多少比它小的
*/
long res1 = 0, res2 = 0;
for (int i = n; i != 0; i--) {
int y = a[i];
res1 += great[i] * (ask(n) - ask(y));
res2 += lower[i] * ask(y - 1);
add(y, 1);
}
System.out.println(res1 + " " + res2);
} }
static int N = (int) (2e5 + 200), n, max_value; static int N = (int) (2e5 + 200), n, max_value;
static int[] c = new int[N]; static int[] c = new int[N];
static int[] a = new int[N]; static int[] a = new int[N];
static long[] l = new long[N]; static long[] great = new long[N];
static long[] r = new long[N]; static long[] lower = new long[N];
static long ask(int x) { static long ask(int x) {
long ans = 0; long ans = 0;
...@@ -35,8 +61,8 @@ public class 楼兰图腾 { ...@@ -35,8 +61,8 @@ public class 楼兰图腾 {
return ans; return ans;
} }
static void add(int i, int value) { static void add(int x, int value) {
for (; i <= n; i += lowbit(i)) { for (int i = x; i <= n; i += lowbit(i)) {
c[i] += value; c[i] += value;
} }
} }
...@@ -45,43 +71,4 @@ public class 楼兰图腾 { ...@@ -45,43 +71,4 @@ public class 楼兰图腾 {
return x & (-x); return x & (-x);
} }
static void res1() {
Arrays.fill(c, 0);
for (int i = n; i != 0; i--) {
r[i] = ask(max_value) - ask(a[i]);
System.out.println(r[i]);
add(a[i], 1);
}
System.out.println();
Arrays.fill(c, 0);
for (int i = 1; i <= n; i++) {
l[i] = ask(max_value) - ask(a[i]);
System.out.println(l[i]);
add(a[i], 1);
}
}
static void res2() {
Arrays.fill(c, 0);
for (int i = n; i != 0; i--) {
r[i] = ask(a[i - 1]);
System.out.println(r[i]);
add(a[i], 1);
}
System.out.println();
Arrays.fill(c, 0);
for (int i = 1; i <= n; i++) {
l[i] = ask(a[i] - 1);
System.out.println(l[i]);
add(a[i], 1);
}
}
static long sumans() {
long ans = 0;
for (int i = 1; i <= n; i++) {
ans += l[i] * r[i];
}
return ans;
}
} }
package 树状数组;
import java.io.*;
import java.util.StringTokenizer;
/**
* https://blog.csdn.net/mrgaohaihang/article/details/107146133
* 有n头奶牛,已知它们的身高为 1~n 且各不相同,但不知道每头奶牛的具体身高。
* 现在这n头奶牛站成一列,已知第i头牛前面有Ai
* 头牛比它低,求每头奶牛的身高。
* 输入格式
* 第1行:输入整数n。
* 第2…n行:每行输入一个整数Ai
* ,第i行表示第i头牛前面有Ai
* 头牛比它低。
* (注意:因为第1头牛前面没有牛,所以并没有将它列出)
* 输出格式
* 输出包含n行,每行输出一个整数表示牛的身高。
* 第i行输出第i头牛的身高。
* 数据范围
* 1≤n≤105
* 输入样例:
* 5
* 1
* 2
* 1
* 0
* 输出样例:
* 2
* 4
* 5
* 3
* 1
* 设A1 A2...An
* 倒着往前推,
* 那么低An位置,就是第An+1小的数
* 第A(i)位置,有A(i)头比它少,就是剩下的第A(i)小的数
* (1)从剩余的数中找到第k小的数
* (2)删除某个数
* 树状数组优化!
* 树状数组,A1~An=1树状数组维护前缀和
* 令树状数组每一个都等于1,代表这个数可用
* sum(x)代表1~x有多少个可用
* 则求第k小的数代表,可以二分求最小的sum(x)的x使得sum(x)=k
*/
public class 谜一样的牛 {
public static void main(String[] args) throws IOException {
n = nextInt();
for (int i = 2; i <= n; i++) {
a[i] = nextInt();
}
for (int i = 1; i <= n; i++) {
add(i, 1);
}
for (int i = n; i != 0; i--) {
int l = 1, r = n, k = a[i] + 1;
while (l < r) {
int mid = (l + r) / 2;
if (ask(mid) >= k) r = mid;
else l = mid + 1;
}
ans[i] = r;
add(r, -1);
}
for (int i = 1; i <= n; i++) {
bw.write(ans[i]+"\n");
}
bw.flush();
}
static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static StringTokenizer st = new StringTokenizer("");
static String next() throws IOException {
while (!st.hasMoreTokens()) {
st = new StringTokenizer(br.readLine());
}
return st.nextToken();
}
static int nextInt() throws IOException {
return Integer.parseInt(next());
}
static int n;
static int[] tr = new int[(int) (1e5 + 10)];
static int[] a = new int[(int) (1e5 + 10)];
static int[] ans = new int[(int) (1e5 + 10)];
static int lowbit(int x) {
return x & -x;
}
static void add(int x, int v) {
for (int i = x; i <= n; i += lowbit(i)) {
tr[i] += v;
}
}
static int ask(int x) {
int res = 0;
while (x != 0) {
res += tr[x];
x -= lowbit(x);
}
return res;
}
}
package 树状数组;
import java.util.Arrays;
import java.util.Scanner;
/**
* https://blog.csdn.net/m0_38033475/article/details/80330157
* 离散化+树状数组求逆序对
* 5
* 13 6 9 11 5
*/
public class 逆序数 {
static class node implements Comparable<node> {
int v, index;
public node(int v, int index) {
this.v = v;
this.index = index;
}
@Override
public int compareTo(node node) {
return v - node.v;
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
for (int i = 1; i <= n; i++) {
de[i] = new node(sc.nextInt(), i);
// max_value = Math.max(a[i], max_value);//记录数组最大值
}
Arrays.sort(de, 1, n+1);
/**
* 正序循环,巧妙想法,也可以求逆序数
* 这里great记录左边有多少个比它大的,
* lower记录左边有多少比它小的
*/
int res = 0;
for (int i = 1; i <= n; i++) {
int y = de[i].index;
res += ask(n) - ask(y);
add(y, 1);
}
System.out.println(res);
}
static int N = (int) (2e5 + 200), n;
static int[] c = new int[N];
static node[] de = new node[N];
static long ask(int x) {
long ans = 0;
while (x != 0) {
ans += c[x];
x -= lowbit(x);
}
return ans;
}
static void add(int x, int value) {
for (int i = x; i <= n; i += lowbit(i)) {
c[i] += value;
}
}
private static int lowbit(int x) {
return x & (-x);
}
}
package 线段树;
//线段树
public class 单点更新区间查询 {
public static void main(String[] args) {
}
static int[] max = new int[800010];
static void build(int k, int l, int r) {
if (l == r) {
return;
}
int mid = l + r >> 1;
build(k << 1, l, mid);
build(k << 1 | 1, mid + 1, r);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册