参考:网络流算法详解-CSDN博客
网络流(二) 最小费用最大流问题-CSDN博客
[网络流]学习笔记:一次理解网络流!-CSDN博客
主要思想就是每次找到一条增广路径,直到网络中不存在增广路径时,就找到了最大流。
对于任意可行流 ff 和任意割 (S,T),其流量 ∣f∣ 不超过割的容量c(S,T),即:
∣f∣≤∑u∈S,v∈Tc(u,v)
论证步骤:
主要思想就是每次找到一条费用最小的增广路径,直到找不到网络中的增广路径时,就找到了最大流的最小费用。
假设当前流 f 是所有流量为 ∣f∣ 的可行流中费用最小的。需证明:通过增广一条费用最小的路径 P,得到流 f′,则 f′f′ 是所有流量为 ∣f∣+Δ的流中费用最小的。
引理:若每次增广的是残余网络中费用最小的路径,则新流的费用仍为当前流量下的最小值。
证明(反证法):
当残余网络中不存在源点到汇点的增广路径时,当前流为最大流。根据归纳法,此时的费用也为最小值
最大流算法和最小费用算法都有贪心的思想,每次迭代都会离最大流网络更近一步。网络流的问题难度主要在抽象建模,很多问题同网络流联系起来并不那边直观。那些可以分成2组节点相互连边的问题通常可以转换为网络流解决,比如二分图匹配。因为引入了反向边,每次找增广路时需要更新正向边和反向边的流量,就需要快查找到增广路径上的边。所以在网络流算法中一般用链式前向星(邻接矩阵不支持两点间连多条边,邻接表不支持快速定位到对应的边)这种数据结构表示图比较高效方便。需要注意反向边的容量为0,花费为负。
题目:E. Soldier and Traveling
https://codeforces.com/problemset/problem/546/E
思路:n个值建对应n个节点,分配给n个位置对应n个节点 。原点到n个节点连边的流量为初始值,n个位置到终点的流量为期望值。需要检测最终的残余网络,输出边的分配情况
复杂度O(V*E^2),证明
https://www.zhihu.com/question/65133832/answer/2306908696
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.StringTokenizer;
public class Main {
public static void main(String args[]) {new Main().run();}
FastReader in = new FastReader();
PrintWriter out = new PrintWriter(System.out);
void run() {
work();
out.flush();
}
long mod=998244353;
long inf=Long.MAX_VALUE/3;
long gcd(long a,long b) {
return a==0?b:gcd(b%a,a);
}
Edge[] edges;
int[] head;
int[] route;
int maxn=220;
int S,T;
int cur;
void addEdge(int s,int e,int flow,int cap){
Edge edge=new Edge(s,e,flow,cap,head[s]);
edges[cur]=edge;
head[s]=cur;
cur++;
}
void work(){
head=new int[maxn];
Arrays.fill(head, -1);
route=new int[maxn];
edges=new Edge[maxn*200];
int n=ni(),m=ni();
int[] A=nia(n);
int[] B=nia(n);
int sum=0,sum1=0;
for(int i=0;i queue=new LinkedList<>();
queue.add(S);
boolean[] vis=new boolean[maxn];
int[] rec=new int[maxn];//当前增广路增加的流量
Arrays.fill(rec,999999999);
Arrays.fill(route,-1);
vis[S]=true;
while(queue.size()>0){
Integer node = queue.poll();
for(int i=head[node];i!=-1;i=edges[i].next){
Edge edge = edges[i];
if(!vis[edge.to]&&edge.flow[] ng(int n, int m) {
ArrayList[] graph=(ArrayList[])new ArrayList[n];
for(int i=0;i();
}
for(int i=1;i<=m;i++) {
int s=in.nextInt()-1,e=in.nextInt()-1;
graph[s].add(e);
graph[e].add(s);
}
return graph;
}
private ArrayList[] ngw(int n, int m) {
ArrayList[] graph=(ArrayList[])new ArrayList[n];
for(int i=0;i();
}
for(int i=1;i<=m;i++) {
long s=in.nextLong()-1,e=in.nextLong()-1,w=in.nextLong();
graph[(int)s].add(new long[] {e,w});
graph[(int)e].add(new long[] {s,w});
}
return graph;
}
private int ni() {
return in.nextInt();
}
private long nl() {
return in.nextLong();
}
private double nd() {
return in.nextDouble();
}
private String ns() {
return in.next();
}
private long[] na(int n) {
long[] A=new long[n];
for(int i=0;i'9'){
b=input.read();
}
while(b>='0'&&b<='9'){
ret=ret*10+(b-'0');
b=input.read();
}
return ret;
}
}catch (Exception e){
e.printStackTrace();
}
return Long.parseLong(next());
}
public double nextDouble()
{
return Double.parseDouble(next());
}
}
找增广路径时每次不需要从头开始搜索,复杂度O(V^2*E),证明讨论 - 力扣(LeetCode)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.StringTokenizer;
public class Main {
public static void main(String args[]) {new Main().run();}
FastReader in = new FastReader();
PrintWriter out = new PrintWriter(System.out);
void run() {
work();
out.flush();
}
long mod=998244353;
int inf=999999999;
long gcd(long a,long b) {
return a==0?b:gcd(b%a,a);
}
Edge[] edges;
int[] head;
int[] route;
int[] deep;
int maxn=220;
int S,T;
int cur;
void addEdge(int s,int e,int flow,int cap){
Edge edge=new Edge(s,e,flow,cap,head[s]);
edges[cur]=edge;
head[s]=cur;
cur++;
}
void work(){
head=new int[maxn];
deep=new int[maxn];
Arrays.fill(head, -1);
route=new int[maxn];
edges=new Edge[maxn*200];
int n=ni(),m=ni();
int[] A=nia(n);
int[] B=nia(n);
int sum=0,sum1=0;
for(int i=0;i queue=new LinkedList<>();
queue.add(S);
Arrays.fill(deep,-1);
deep[S]=0;
while(queue.size()>0){
Integer node = queue.poll();
for(int i=head[node];i!=-1;i=edges[i].next){
Edge edge = edges[i];
if(deep[edge.to]==-1&&edge.flow[] ng(int n, int m) {
ArrayList[] graph=(ArrayList[])new ArrayList[n];
for(int i=0;i();
}
for(int i=1;i<=m;i++) {
int s=in.nextInt()-1,e=in.nextInt()-1;
graph[s].add(e);
graph[e].add(s);
}
return graph;
}
private ArrayList[] ngw(int n, int m) {
ArrayList[] graph=(ArrayList[])new ArrayList[n];
for(int i=0;i();
}
for(int i=1;i<=m;i++) {
long s=in.nextLong()-1,e=in.nextLong()-1,w=in.nextLong();
graph[(int)s].add(new long[] {e,w});
graph[(int)e].add(new long[] {s,w});
}
return graph;
}
private int ni() {
return in.nextInt();
}
private long nl() {
return in.nextLong();
}
private double nd() {
return in.nextDouble();
}
private String ns() {
return in.next();
}
private long[] na(int n) {
long[] A=new long[n];
for(int i=0;i'9'){
b=input.read();
}
while(b>='0'&&b<='9'){
ret=ret*10+(b-'0');
b=input.read();
}
return ret;
}
}catch (Exception e){
e.printStackTrace();
}
return Long.parseLong(next());
}
public double nextDouble()
{
return Double.parseDouble(next());
}
}
题目:863F - Almost Permutation
https://codeforces.com/contest/863/problem/F
该题的关键在于构造图,构造1个节点(下标0)代表原点S,n个节点(下标1到n)代表从1到n的n个值,n个节点(下标n+1到2n)代表从1到n的n个位置,1个终点T。其中原点到每个下标1到n的点都连50条边,费用为2*j-1,(j表示第j条边),下标1到n的点根据题目条件限制分别连上多条边到下标n+1到2*n的点,每个下标n+1到2*n的点在连一条边到终点,其中流量都为1。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.StringTokenizer;
public class Main {
public static void main(String args[]) {new Main().run();}
FastReader in = new FastReader();
PrintWriter out = new PrintWriter(System.out);
void run() {
work();
out.flush();
}
long mod=998244353;
long inf=Long.MAX_VALUE/3;
long gcd(long a,long b) {
return a==0?b:gcd(b%a,a);
}
Edge[] edges;
int[] head;
int[] route;
int maxn=120;
int S,T;
int cur;
void addEdge(int s,int e,int flow,int cap,int cost){
Edge edge=new Edge(s,e,flow,cap,cost,head[s]);
edges[cur]=edge;
head[s]=cur;
cur++;
}
void work(){
int n=ni();
edges=new Edge[maxn*100];
head=new int[maxn];
Arrays.fill(head,-1);
route=new int[maxn];
boolean[][] rec=new boolean[n+1][n+1];
for(int q=ni();q>0;q--){
int t=ni(),l=ni(),r=ni(),v=ni();
if(t==1){
for(int i=l;i<=r;i++){
for(int j=1;j queue=new LinkedList<>();
queue.add(S);
boolean[] vis=new boolean[maxn];
int[] rec=new int[maxn];
Arrays.fill(rec,999999999);
Arrays.fill(route,-1);
rec[S]=0;
while(queue.size()>0){
Integer node = queue.poll();
vis[node]=false;
for(int i=head[node];i!=-1;i=edges[i].next){
Edge edge = edges[i];
if(rec[edge.to]>rec[node]+edge.cost&&edge.flow[] ng(int n, int m) {
ArrayList[] graph=(ArrayList[])new ArrayList[n];
for(int i=0;i();
}
for(int i=1;i<=m;i++) {
int s=in.nextInt()-1,e=in.nextInt()-1;
graph[s].add(e);
graph[e].add(s);
}
return graph;
}
private ArrayList[] ngw(int n, int m) {
ArrayList[] graph=(ArrayList[])new ArrayList[n];
for(int i=0;i();
}
for(int i=1;i<=m;i++) {
long s=in.nextLong()-1,e=in.nextLong()-1,w=in.nextLong();
graph[(int)s].add(new long[] {e,w});
graph[(int)e].add(new long[] {s,w});
}
return graph;
}
private int ni() {
return in.nextInt();
}
private long nl() {
return in.nextLong();
}
private double nd() {
return in.nextDouble();
}
private String ns() {
return in.next();
}
private long[] na(int n) {
long[] A=new long[n];
for(int i=0;i'9'){
b=input.read();
}
while(b>='0'&&b<='9'){
ret=ret*10+(b-'0');
b=input.read();
}
return ret;
}
}catch (Exception e){
e.printStackTrace();
}
return Long.parseLong(next());
}
public double nextDouble()
{
return Double.parseDouble(next());
}
}