Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
qq_36480062
Algorithm
提交
0a435d14
A
Algorithm
项目概览
qq_36480062
/
Algorithm
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
A
Algorithm
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
0a435d14
编写于
9月 20, 2020
作者:
qq_36480062
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
c
上级
300f98cc
变更
9
隐藏空白更改
内联
并排
Showing
9 changed file
with
487 addition
and
30 deletion
+487
-30
ACWing/src/graph/Floyd/Floyd.md
ACWing/src/graph/Floyd/Floyd.md
+25
-1
ACWing/src/graph/Floyd/排序.java
ACWing/src/graph/Floyd/排序.java
+204
-0
ACWing/src/graph/Floyd/最小环.java
ACWing/src/graph/Floyd/最小环.java
+36
-0
ACWing/src/graph/Floyd/牛的旅行.java
ACWing/src/graph/Floyd/牛的旅行.java
+136
-8
ACWing/src/graph/差分约束/区间.java
ACWing/src/graph/差分约束/区间.java
+22
-6
ACWing/src/graph/差分约束/差分约束.md
ACWing/src/graph/差分约束/差分约束.md
+22
-8
ACWing/src/graph/差分约束/排队布局.java
ACWing/src/graph/差分约束/排队布局.java
+30
-4
ACWing/src/graph/差分约束/糖果.java
ACWing/src/graph/差分约束/糖果.java
+10
-3
ACWing/src/graph/差分约束/雇佣收银员.java
ACWing/src/graph/差分约束/雇佣收银员.java
+2
-0
未找到文件。
ACWing/src/graph/Floyd/Floyd.md
浏览文件 @
0a435d14
...
...
@@ -31,4 +31,28 @@ dp分析:
i~k取最小且k~j取最小:d[k-1][i][k]+d[k-1][k][j]
所有路径不包含节点k的路径:d[k-1][i][j]
直接去掉最高维,代码等价
d[i,j]=min( d[i,j] , d[i,k]+d[k,j] )
\ No newline at end of file
d[i,j]=min( d[i,j] , d[i,k]+d[k,j] )
传递闭包
给定一个有向图,无向图也可以看做有向图
a可以到b,b可以到c,
那么给a到c连一条边
所有能间接到达的点,都给它们连一条直接的边!
所有这样的边都连上,就成为原图的传递闭包
Floyd可以用O(n^3)求出传递闭包,邻接矩阵
1.初始化d(i,j)=g(i,j)
g(i,j){
存在i->j这条边 1
不存在i->j这条边 0
}
2.Floyd
for k
for i
for j
if( d(i,k)=1 & d(k,j)=1)
d(i,j)=1
如果i能到k,k能到j,说明i能到j,那么把d(i,j)置为1
说明g(i,j)可以间接到达,连一条边
ACWing/src/graph/Floyd/排序.java
0 → 100644
浏览文件 @
0a435d14
package
graph.Floyd
;
import
java.util.Arrays
;
import
java.util.Scanner
;
/**
*https://blog.csdn.net/qq_30277239/article/details/107453694
* 给定 n 个变量和 m 个不等式。其中 n 小于等于26,变量分别用前 n 的大写英文字母表示。
* 不等式之间具有传递性,即若 A>B 且 B>C ,则 A>C。
* 请从前往后遍历每对关系,每次遍历时判断:
* 如果能够确定全部关系且无矛盾,则结束循环,输出确定的次序;
* 如果发生矛盾,则结束循环,输出有矛盾;
* 如果循环结束时没有发生上述两种情况,则输出无定解。
* 输入格式
* 输入包含多组测试数据。
* 每组测试数据,第一行包含两个整数n和m。
* 接下来m行,每行包含一个不等式,不等式全部为小于关系。
* 当输入一行0 0时,表示输入终止。
* 输出格式
* 每组数据输出一个占一行的结果。
* 结果可能为下列三种之一:
* 如果可以确定两两之间的关系,则输出 "Sorted sequence determined after t relations: yyy...y.",其中't'指迭代次数,'yyy...y'是指升序排列的所有变量。
* 如果有矛盾,则输出: "Inconsistency found after t relations.",其中't'指迭代次数。
* 如果没有矛盾,且不能确定两两之间的关系,则输出 "Sorted sequence cannot be determined."。
* 数据范围
* 2≤n≤26,变量只可能为大写字母A~Z。
* 输入样例1:
* 4 6
* A<B
* A<C
* B<C
* C<D
* B<D
* A<B
* 3 2
* A<B
* B<A
* 26 1
* A<Z
* 0 0
* 输出样例1:
* Sorted sequence determined after 4 relations: ABCD.
* Inconsistency found after 2 relations.
* Sorted sequence cannot be determined.
* 输入样例2:
* 6 6
* A<F
* B<D
* C<E
* F<D
* D<E
* E<F
* 0 0
* 输出样例2:
* Inconsistency found after 6 relations.
* 输入样例3:
* 5 5
* A<B
* B<C
* C<D
* D<E
* E<A
* 0 0
* 输出样例3:
* Sorted sequence determined after 4 relations: ABCDE.
* 本题考察Floyd算法在传递闭包问题上的应用。给定若干对元素和若干对二元关系,并且关系具有传递性,通过传递性推导出尽量多的元素之间的关系的问题被称为传递闭包。比如a < b,b < c,就可以推导出a < c,如果用图形表示出这种大小关系,就是a到b有一条有向边,b到c有一条有向边,可以推出a可以到达c,找出图中各点能够到达点的集合,就类似于Floyd算法求图中任意两点间的最短距离。Floyd求解传递闭包问题的代码如下:
* void floyd(){
* for(int k = 0;k < n;k++)
* for(int i = 0;i < n;i++)
* for(int j = 0;j < n;j++)
* f[i][j] |= f[i][k] & f[k][j];
* }
* 只是对原来算法在状态转移方程上略加修改 就能够求解传递闭包问题了。
* f[i][j] = 1表示i可以到达j ( i < j),f[i][j] = 0表示i不可到达j。
* 只要i能够到达k并且k能够到达j,那么i就能够到达j,这就是上面代码的含义。
* 对于本题而言,给定n个元素和一堆二元关系,依次读取每个二元关系,
* 在读取第i个二元关系后,如果可以确定n个元素两两间的大小关系了,
* 就输出在几对二元关系后可以确定次序,并且次序是什么;如果出现了矛盾,
* 就是A < B并且B < A这种情况发生了就输出多少对二元关系后开始出现矛盾;
* 如果遍历完所有的二元关系还不能确定所有元素间的大小关系,就输出无法确定。
* 可以发现,题目描述要求按顺序遍历二元关系,一旦前i个二元关系可以确定次序了就不再遍历了,
* 即使第i + 1对二元关系就会出现矛盾也不去管它了。对于二元关系的处理和之前的做法一样,
* A < B,就将f[0][1]设为1,题目字母只会在A到Z间,因此可以映射为0到25这26个元素,
* 如果f[0][1] = f[1][0] = 1,就可以推出f[0][0] = 1,此时A < B并且A > B发生矛盾,
* 因此在f[i][i]= 1时发生矛盾。
* 下面详细分析下求解的步骤:首先每读取一对二元关系,就执行一遍Floyd算法求传递闭包,
* 然后执行check函数判断下此时是否可以终止遍历,如果发生矛盾或者次序全部被确定就终止遍历,
* 否则继续遍历。在确定所有的次序后,需要输出偏序关系,因此需要执行下getorder函数。
* 注意这里的终止遍历仅仅是不再针对新增的二元关系去求传递闭包,循环还是要继续的,
* 需要读完数据才能继续读下一组数据。
* 下面设计check函数和getorder函数。
* int check(){
* for(int i = 0;i < n;i++)
* if(f[i][i]) return 0;
* for(int i = 0;i < n;i++){
* for(int j = 0;j < i;j++){
* if(!f[i][j] && !f[j][i]) return 1;
* }
* }
* return 2;
* }
* 如果f[i][i] = 1就发生矛盾了,可以返回了;
* 如果f[i][j] = f[j][i] = 0表示i与j之间的偏序关系还没有确定下来,
* 就需要继续读取下一对二元关系;如果所有的关系都确定了,就返回2。
* string getorder(){
* char s[26];
* for(int i = 0;i < n;i++){
* int cnt = 0;
* for(int j = 0;j < n;j++) cnt += f[i][j];
* s[n - cnt - 1] = i + 'A';
* }
* return string(s,s + n);
* }
* 确定所有元素次序后如何判断元素i在第几个位置呢?f[i][j] = 1表示i < j,
* 因此计算下下i小于元素的个数cnt,就可以判定i是第cnt + 1大的元素了。
*
* 1.显然如果A<A自己小于自己就是矛盾,对应图论就是d[i,i]=1
* A<B小于看做 A->B的边为1 d(A,B)=1
* 2.唯一确定,d(i,j) i不等于j d(i,j) 和d(j,i)只有一个是1
* 3.顺序不唯一
*
*
*/
public
class
排序
{
public
static
void
main
(
String
[]
args
)
{
Scanner
sc
=
new
Scanner
(
System
.
in
);
while
(
sc
.
hasNext
())
{
int
type
=
0
,
t
=
0
;
n
=
sc
.
nextInt
();
m
=
sc
.
nextInt
();
for
(
int
i
=
0
;
i
<
26
;
i
++)
{
Arrays
.
fill
(
g
[
i
],
false
);
}
for
(
int
i
=
1
;
i
<=
m
;
i
++)
{
String
s
=
sc
.
next
();
int
a
=
s
.
charAt
(
0
)
-
'A'
,
b
=
s
.
charAt
(
2
)
-
'A'
;
if
(
type
==
0
)
{
g
[
a
][
b
]
=
true
;
floyd
();
type
=
check
();
if
(
type
!=
0
)
t
=
i
;
}
}
if
(
type
!=
0
)
{
System
.
out
.
print
(
"Sorted sequence cannot be determined.\n"
);
}
else
if
(
type
==
2
)
System
.
out
.
printf
(
"Inconsistency found after %d relations.\n"
,
t
);
else
{
Arrays
.
fill
(
st
,
false
);
System
.
out
.
printf
(
"Sorted sequence determined after %d relations:"
,
t
);
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
System
.
out
.
printf
(
"%c"
,
getmin
());
}
System
.
out
.
println
(
"."
);
}
}
}
static
int
n
,
m
,
N
=
26
;
static
boolean
[][]
g
=
new
boolean
[
N
][
N
],
d
=
new
boolean
[
N
][
N
];
static
boolean
[]
st
=
new
boolean
[
N
];
static
char
getmin
()
{
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
if
(!
st
[
i
])
{
boolean
f
=
true
;
for
(
int
j
=
0
;
j
<
n
;
j
++)
{
if
(!
st
[
i
]
&&
d
[
j
][
i
])
{
f
=
false
;
break
;
}
}
if
(
f
)
{
st
[
i
]
=
true
;
return
(
char
)
(
'A'
+
i
);
}
}
}
return
' '
;
}
static
void
floyd
()
{
d
=
Arrays
.
copyOf
(
g
,
d
.
length
);
for
(
int
k
=
0
;
k
<
n
;
k
++)
{
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
for
(
int
j
=
0
;
j
<
n
;
j
++)
{
d
[
i
][
j
]
|=
d
[
i
][
k
]
&&
d
[
k
][
j
];
}
}
}
}
static
int
check
()
{
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
if
(
d
[
i
][
i
])
return
2
;
}
//i<i矛盾
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
for
(
int
j
=
0
;
j
<
n
;
j
++)
{
if
(!
d
[
i
][
j
]
&&
!
d
[
j
][
i
])
return
0
;
}
}
return
1
;
}
}
ACWing/src/graph/Floyd/最小环.java
0 → 100644
浏览文件 @
0a435d14
package
graph.Floyd
;
/**
* https://blog.csdn.net/qq_30277239/article/details/107702663
* 给定一张无向图,求图中一个至少包含3个点的环,环上的节点不重复,并且环上的边的长度之和最小。
* 该问题称为无向图的最小环问题。
* 你需要输出最小环的方案,若最小环不唯一,输出任意一个均可。
* 输入格式
* 第一行包含两个整数N和M,表示无向图有N个点,M条边。
* 接下来M行,每行包含三个整数u,v,l,表示点u和点v之间有一条边,边长为l。
* 输出格式
* 输出占一行,包含最小环的所有节点(按顺序输出),如果不存在则输出’No solution.’。
* 数据范围
* 1≤N≤100,
* 1≤M≤10000,
* 1≤l<500
* 输入样例:
* 5 7
* 1 4 1
* 1 3 300
* 3 1 10
* 1 2 16
* 2 3 100
* 2 5 15
* 5 3 20
* 输出样例:
* 1 3 5 2
* 如图所示,设环上编号最大的节点编号是k,i和j分别是环上与k相邻的两个节点。根据之前对floyd算法的推导,f[k][i][j]表示图上i经过编号不超过k的节点到达j的最短路径长度,观察上图可以发现,环的长度等于i到j的最短距离加上g[i][k]再加上g[k][j],设环的长度为ans,则ans = f[i][j] + g[i][k] + g[k][j],这里的f[i][j]是对f[k-1][i][j]降维后的结果,为什么是f[k-1][i][j]而不是f[k][i][j],因为前面已经规定k是最大的节点,环上其他的节点必然都小于k。为什么要这么规定,这种思路是如何来的?后面会分析这种思路的由来。
*
* 我们知道,floyd算法最外层k刚刚循环到第k次时,f[i][j]存储的还是i经过编号不超过k-1的节点到达j的最短路径长度,此时的f[i][j]正是我们所需要的,因此在第k次循环开始时,由k,i,j加上i到j中间的节点构成的最小环长度就是f[i][j] + g[i][k] + g[k][j],我们还是用三层循环去枚举所有的情况,最终就可以求出最小环的长度。
*/
public
class
最小环
{
public
static
void
main
(
String
[]
args
)
{
}
}
ACWing/src/graph/Floyd/牛的旅行.java
浏览文件 @
0a435d14
package
graph.Floyd
;
import
java.util.Scanner
;
/**
* https://blog.csdn.net/qq_30277239/article/details/107301496
* https://www.cnblogs.com/ctyakwf/p/12829458.html
* 农民John的农场里有很多牧区,有的路径连接一些特定的牧区。
* 一片所有连通的牧区称为一个牧场。
* 但是就目前而言,你能看到至少有两个牧区不连通。
* 现在,John想在农场里添加一条路径(注意,恰好一条)。
* 一个牧场的直径就是牧场中最远的两个牧区的距离(本题中所提到的所有距离指的都是最短的距离)。
* 考虑如下的两个牧场,每一个牧区都有自己的坐标:
* 图 1 是有 5 个牧区的牧场,牧区用“*”表示,路径用直线表示。
* 图 1 所示的牧场的直径大约是 12.07106, 最远的两个牧区是 A 和 E,
* 它们之间的最短路径是 A-B-E。
* 图 2 是另一个牧场。
* 这两个牧场都在John的农场上。
* John将会在两个牧场中各选一个牧区,然后用一条路径连起来,
* 使得连通后这个新的更大的牧场有最小的直径。
* 注意,如果两条路径中途相交,我们不认为它们是连通的。
* 只有两条路径在同一个牧区相交,我们才认为它们是连通的。
* 现在请你编程找出一条连接两个不同牧场的路径,使得连上这条路径后,所有牧场(生成的新牧场和原有牧场)中直径最大的牧场的直径尽可能小。
* 输出这个直径最小可能值。
* 输入格式
* 第 1 行:一个整数 N, 表示牧区数;
* 第 2 到 N+1 行:每行两个整数 X,Y, 表示 N 个牧区的坐标。每个牧区的坐标都是不一样的。
* 第 N+2 行到第 2*N+1 行:每行包括 N 个数字 ( 0或1 ) 表示一个对称邻接矩阵。
* 例如,题目描述中的两个牧场的矩阵描述如下:
* A B C D E F G H
* A 0 1 0 0 0 0 0 0
* B 1 0 1 1 1 0 0 0
* C 0 1 0 0 1 0 0 0
* D 0 1 0 0 1 0 0 0
* E 0 1 1 1 0 0 0 0
* F 0 0 0 0 0 0 1 0
* G 0 0 0 0 0 1 0 1
* H 0 0 0 0 0 0 1 0
* 输入数据中至少包括两个不连通的牧区。
* 输出格式
* 只有一行,包括一个实数,表示所求答案。
* 数字保留六位小数。
* 数据范围
* 1≤N≤150,
* 0≤X,Y≤10^5
* 输入样例:
* 8
* 10 10
* 15 10
* 20 10
* 15 15
* 20 15
* 30 15
* 25 10
* 30 10
* 01000000
* 10111000
* 01001000
* 01001000
* 01110000
* 00000010
* 00000101
* 00000010
* 输出样例:
* 22.071068
*/
public
class
牛的旅行
{
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
)
/
1
e9
>=
0.98
)
break
;
}
System
.
out
.
println
(
i
);
Scanner
sc
=
new
Scanner
(
System
.
in
);
n
=
sc
.
nextInt
();
int
a
,
b
;
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
a
=
sc
.
nextInt
();
b
=
sc
.
nextInt
();
q
[
i
]
=
new
node
(
a
,
b
);
}
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
g
[
i
]
=
sc
.
next
().
toCharArray
();
}
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
for
(
int
j
=
0
;
j
<
n
;
j
++)
{
if
(
i
!=
j
)
{
if
(
g
[
i
][
j
]
==
'1'
)
d
[
i
][
j
]
=
get_dist
(
q
[
i
],
q
[
j
]);
else
d
[
i
][
j
]
=
inf
;
}
}
}
//floyd
for
(
int
k
=
0
;
k
<
n
;
k
++)
{
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
for
(
int
j
=
0
;
j
<
n
;
j
++)
{
d
[
i
][
j
]
=
Math
.
min
(
d
[
i
][
j
],
d
[
i
][
k
]
+
d
[
k
][
j
]);
}
}
}
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
for
(
int
j
=
0
;
j
<
n
;
j
++)
{
if
(
d
[
i
][
j
]
<
inf
)
{
maxd
[
i
]
=
Math
.
max
(
maxd
[
i
],
d
[
i
][
j
]);
}
}
}
double
res1
=
0
;
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
res1
=
Math
.
max
(
res1
,
maxd
[
i
]);
}
double
res2
=
inf
;
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
for
(
int
j
=
0
;
j
<
n
;
j
++)
{
if
(
d
[
i
][
j
]
>=
inf
)
{
res2
=
Math
.
min
(
res2
,
get_dist
(
q
[
i
],
q
[
j
])
+
maxd
[
i
]
+
maxd
[
j
]);
}
}
}
System
.
out
.
printf
(
"%.6f"
,
Math
.
max
(
res1
,
res2
));
}
static
double
get_dist
(
node
a
,
node
b
)
{
double
dx
=
a
.
x
-
b
.
x
,
dy
=
a
.
y
-
b
.
y
;
return
Math
.
sqrt
(
dx
*
dx
+
dy
*
dy
);
}
static
int
n
,
N
=
150
;
static
double
inf
=
1
e20
;
static
node
[]
q
=
new
node
[
N
];
static
char
[][]
g
=
new
char
[
N
][
N
];
static
double
[][]
d
=
new
double
[
N
][
N
];
//邻接矩阵
static
double
[]
maxd
=
new
double
[
N
];
static
class
node
{
int
x
,
y
;
public
node
(
int
x
,
int
y
)
{
this
.
x
=
x
;
this
.
y
=
y
;
}
}
}
ACWing/src/graph/差分约束/区间.java
浏览文件 @
0a435d14
...
...
@@ -23,6 +23,17 @@ import java.util.Scanner;
* 输出样例:
* 6
* 该题必定有解,显然全放进去,一定满足要求
* 显然0~50000 下标不好用,同时+1, 得到1~50001
* 往右平移一格,不影响答案,空出0,
* 利用前缀和,
* S0=0
* Si表示从1~i中被选出的数的个数
* 则S50001的最小值,表示1~50001被选出数的个数,则是答案
* 找不等关系,显然:
* 1. Si>=S(i-1) 1<=i<=50001 i-1向i连一条长度为0的边,这个条件存在意味着,存在源点能遍历到所有点,所有边
* 2. Si-Si-1<=1 => S(i-1)>=Si-1 表示第i个数选没选,显然每个数只能选一次
* 3. [a,b]中必须选c个, =>Sb-S(a-1)>=c => Sb>=S(a-1)+C
* 令S50001最小,最长路
* 思路:
* 先将不等式写出来
* 将所有区间向右移一位,这样如果1-2有一条边 3-4有一条边,
...
...
@@ -45,7 +56,9 @@ public class 区间 {
n
=
sc
.
nextInt
();
for
(
int
i
=
1
;
i
<=
50001
;
i
++)
{
add
(
i
-
1
,
i
,
0
);
//Si>=S(i-1)
add
(
i
,
i
-
1
,
-
1
);
//S(i-1)>=Si-1
}
int
a
,
b
,
c
;
while
(
n
--
!=
0
)
{
...
...
@@ -55,14 +68,17 @@ public class 区间 {
a
++;
b
++;
add
(
a
-
1
,
b
,
c
);
//Sb>=S(a-1)+C
}
spfa
();
System
.
out
.
println
(
dis
[
50001
]);
}
private
static
void
spfa
()
{
int
inf
=
Integer
.
MIN_VALUE
/
2
-
10000
;
q
.
add
(
0
);
Arrays
.
fill
(
dis
,
-
Integer
.
MAX_VALUE
/
2
);
Arrays
.
fill
(
dis
,
inf
);
//求最小值,所以初始化为负无穷,看三角不等式,更新规则,得出
st
[
0
]
=
true
;
dis
[
0
]
=
0
;
while
(!
q
.
isEmpty
())
{
...
...
@@ -82,7 +98,7 @@ public class 区间 {
}
static
int
n
,
m
,
N
=
50010
,
M
=
150010
,
count
=
1
;
static
int
n
,
m
,
N
=
50010
,
M
=
150010
,
idx
=
1
;
static
int
[]
h
=
new
int
[
N
];
static
int
[]
e
=
new
int
[
M
];
static
int
[]
ne
=
new
int
[
M
];
...
...
@@ -92,9 +108,9 @@ public class 区间 {
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
++;
e
[
idx
]
=
b
;
w
[
idx
]
=
c
;
ne
[
idx
]
=
h
[
a
];
h
[
a
]
=
idx
++;
}
}
ACWing/src/graph/差分约束/差分约束.md
浏览文件 @
0a435d14
...
...
@@ -3,6 +3,8 @@
```
1:求不等式的可行解,
源点满足,从源点出发,一定可以走到所有的边
只有从源点出发能到达的边,才是满足不等式的
没考虑到那条边,就是没用到那条边,没满足某个不等式
2:求最大值或最小值
```
````
...
...
@@ -34,29 +36,40 @@ Xi<=Xj+Ck
将不等式格式化,转化为j走到i长度为Ck的边
不一定走到所有点,但一定要能走到所有边,限制的是边
孤立的点也就是没有限制,
2:找到虚拟源点,使得虚拟源点可以到达所有的边
3:从源点求单源最短路
如果存在负环,说明不等式组无解!
如:X1->X2->X1 权值之和小于0
X2<=X1+C1
X1<=X2+C2
放缩X2<=X2+C2+C1
显然矛盾
X3<=X2+C2
X1<=X3+C3
放缩X2<=X3+c3+C2+C1
显然矛盾,原不等式组无解
不等式无解等价于图存在负环
如果没有负环,则dis[i]就是可行解
做一个对偶Xk>=Xi-Ck
i->连一条-Ck的边
dis[j]>=dis[i]+Ck求最长路
如果是求的最小值,求最长路,
如果求的是最长值,应该求最短路
如果是求的最小值,求最长路,求出每个变量X1,X2..的最小值
如果求的是最大值,应该求最短路 求出每个变量X1,X2..的最大值
应该有绝对关系,比如Xi>=0
转化Xi<=c c为常数,这类的不等式
如果没有无法求出最值,只能求出相对关系,把不等式组同时+d也满足
转化Xi<=c ,或者Xi>=c,c为常数,这类的不等式,如何变成边呢?
建立超级源点为X0到i长度是0的边
Xi<=C转化为Xi<=X0+C
构成不等式链对应路径,Xi<=Xj+C1<=Xk+C2+C1<=...<=X0+C1+C2...
放缩到最后变成,0+C1+C2+...上界
构成的不等式链求的是所有上界的最小值
Xi的最大值为一个所有上界的最小值等价于求0到i路径的最小值
以求最大值为例,所有从Xi出发,
构成不等式链 Xi<=Xj+C1<=Xk+C2+C1<=...<=C1+C2+C3+..最后一定是个常数
最终Xi的最大值为一个所有上界的最小值,,,等价于求0到i路径的最小值
每一个这样的不等式链,都是从0(超级源点)出发
\ No newline at end of file
ACWing/src/graph/差分约束/排队布局.java
浏览文件 @
0a435d14
...
...
@@ -25,9 +25,31 @@ import java.util.Scanner;
* 2≤N≤1000,
* 1≤ML,MD≤104,
* 1≤L,D≤106
* 输入样例:
* 4 2 1
* 1 3 10
* 2 4 20
* 2 3 3
* 输出样例:
* 27
* 思路
* 不等式,并且使所有点联通,i<=i+1+0
* 加入i+1向i的边
* 题中求最大值,则用最短路,求出所有上界的最小值
* 首先关系先找全列出来:
* <p>
* 找出所有不等式
* 第一种关系:Xb-Xa<=L 转换后:Xb<=Xa+L ; 也就是 Xa—>Xb (权值为L)
* 第二种关系:Xb-Xa>=D 转换后:Xa<=Xb-D ; 也就是 Xb—>Xa (权值为D)
* 第三种关系:Xi<=X(i+1) 1<=i<n Xi<=X(i+1)+0
* 不存在超级源点,所以建立超级源点X0,假定所有的Xi都处于数轴右边
* 假定Xi<=X0 X0->Xi (权值为0) X0向所有边连长度为0的边
* 实现不用建立超级源点到所有点的边,因为第一步把超级源点放进队列,
* spfa就会把所有邻边都加进队列,所以直接加入队列并标记访问
* 把X1固定成0,然后看Xn是不是无限大,等价
* 有一个链式关系,但X1和Xn没有关系,所以没有任何限制,就是正无穷
* <p>
* 1.第一个问题,首先做一遍spfa判断一下有没有负环,此时可以假设虚拟源点为0;
* 2.然后再做一次spfa判断1到n的距离是否为正无穷;注意此时源点为1;
* 3.第三个问题,如果不满足以上两个条件则输出dis[n]即可;
*/
public
class
排队布局
{
public
static
void
main
(
String
[]
args
)
{
...
...
@@ -37,6 +59,7 @@ public class 排队布局 {
int
m2
=
sc
.
nextInt
();
for
(
int
i
=
1
;
i
<
n
;
i
++)
{
add
(
i
+
1
,
i
,
0
);
//Xi<=X(i+1)
}
int
a
,
b
,
c
,
t
;
while
(
m1
--
!=
0
)
{
...
...
@@ -49,6 +72,7 @@ public class 排队布局 {
a
=
t
;
}
add
(
a
,
b
,
c
);
//Xb<=Xa+L
}
while
(
m2
--
!=
0
)
{
a
=
sc
.
nextInt
();
...
...
@@ -60,10 +84,11 @@ public class 排队布局 {
a
=
t
;
}
add
(
b
,
a
,
-
c
);
//Xb<=Xa-D
}
if
(!
spfa
(
n
))
System
.
out
.
println
(-
1
);
else
{
spfa
(
1
);
spfa
(
1
);
//从1开始往下找,看能不能找到n,看X1能否限制Xn,无法限制,则Xn最大正无穷
if
(
dis
[
n
]
==
Integer
.
MAX_VALUE
/
2
)
System
.
out
.
println
(-
2
);
else
System
.
out
.
println
(
dis
[
n
]);
}
...
...
@@ -74,6 +99,7 @@ public class 排队布局 {
Arrays
.
fill
(
st
,
false
);
Arrays
.
fill
(
cnt
,
0
);
q
.
clear
();
for
(
int
i
=
1
;
i
<=
size
;
i
++)
{
dis
[
i
]
=
0
;
q
.
add
(
i
);
...
...
@@ -87,7 +113,7 @@ public class 排队布局 {
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
(
cnt
[
j
]
>=
n
)
return
false
;
//判断是否存在负环!!!
if
(!
st
[
j
])
{
q
.
add
(
j
);
st
[
j
]
=
true
;
...
...
ACWing/src/graph/差分约束/糖果.java
浏览文件 @
0a435d14
...
...
@@ -38,6 +38,7 @@ import java.util.Scanner;
* 输出样例:
* 11
* 思路:
* 最小值->最长路
* 因为我们要求的是最小值且约束条件可转换为若干不等式,所以我们可以用差分约束来做,求最长路,不等式为大于号。
* 将题目转换成一堆不等式
* x=1=》A>=B&&B>=A
...
...
@@ -63,17 +64,23 @@ public class 糖果 {
if
(
x
==
1
)
{
add
(
b
,
a
,
0
);
add
(
a
,
b
,
0
);
//A>=B,B>=A
}
else
if
(
x
==
2
)
{
add
(
a
,
b
,
1
);
//B>=A+1
}
else
if
(
x
==
3
)
{
add
(
b
,
a
,
0
);
//A>=B
}
else
if
(
x
==
4
)
{
add
(
b
,
a
,
1
);
//A>=B+1
}
else
add
(
a
,
b
,
0
);
//B>=A
}
for
(
int
i
=
1
;
i
<=
n
;
i
++)
{
add
(
0
,
i
,
1
);
}
}
//所有的小朋友至少分到1个糖
if
(!
spfa
())
System
.
out
.
println
(
"-1"
);
else
{
long
res
=
0
;
...
...
@@ -111,12 +118,12 @@ public class 糖果 {
static
int
finf
=
Integer
.
MIN_VALUE
/
2
;
static
int
n
,
m
,
N
=
100010
,
M
=
300010
,
count
=
1
;
static
int
n
,
m
,
N
=
100010
,
M
=
300010
,
count
=
1
;
//最坏建3倍边,因为第一种双向边,每个点和超级源点的边
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
long
[]
dis
=
new
long
[
N
];
static
int
[]
cnt
=
new
int
[
N
];
static
boolean
[]
st
=
new
boolean
[
N
];
static
ArrayDeque
<
Integer
>
q
=
new
ArrayDeque
<
Integer
>();
...
...
ACWing/src/graph/差分约束/雇佣收银员.java
浏览文件 @
0a435d14
package
graph.
差分约束
;
/**
* https://blog.csdn.net/weixin_45080867/article/details/108041493
* https://blog.csdn.net/qq_44828887/article/details/107291492
*
*/
public
class
雇佣收银员
{
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录