假如你已經知道了什麼是最大流了。
我們直到,從 源點 到 匯點 的 最大流,我們確信這個值是唯一的,一定有最大流的存在。
那麼最小花費最大流也就是:
這個最大的值,可能有多個情況,從源點 經過了不同的路徑到達的 匯點,縱使最後得到的最大流值相同。 仍然可能有 不同的路徑,假如每個路徑的代價還不同,這意味着,我們一邊要找最大流,一邊還要確保我們走的路徑代價要儘可能低。這兩個因素導致我們不確定該不該走這一個邊,感覺好麻煩。。。
不用着急。
這時候,我們先不想這個問題,我們考慮簡簡單單的最短路問題。
想必你寫過最短路, 我們從spfa這個算法入手。
我們把源點當做起點,匯點當做終點,請注意,我們把 邊的單位代價
當做邊長,也就是 邊的代價/邊容量
而不是邊的容量
!, 先走一遍spfa ,ok,現在,是不是一定有 dis[] 數組了,對嗎。別忘了,我們最短路最後就是得出一個dis數組來的。 這也意味着,我們有了一條最小路徑了。( 我們在 更新dis[] 的時候,順帶更新一個pre 數組就行了, 用來記錄最短路徑的。。)。
好了,這時候我們要做一件最重要的事情,也就是算法的核心
我們找找這天路徑上面,容量最小的那個邊
真是妙,我們只用找這路上最小容量,必定就是 這個路徑的最大流了。
然後 , 最小容量 x 路徑長 , 這不就是這條路徑的整個代價了嘛!
以此類推,通過更新邊容量之後,在進行第二次,第三次,第四次,spfa ,直到找不到源點到匯點的最短路徑了。
總結:
核心是,把一個邊的 單位代價 ,當做路徑長,跑一遍spfa。得到一個結果
然後把一些邊的容量更新了( 一些邊的容量變小), 再跑,,直到沒到匯點的路徑了。。
自己的代碼:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
import java.util.StringTokenizer;
//
public class 最大流最小路徑模板 {
static class InputReader
{
BufferedReader buf;
StringTokenizer tok;
InputReader()
{
buf = new BufferedReader(new InputStreamReader(System.in));
}
boolean hasNext()
{
while(tok == null || !tok.hasMoreElements())
{
try
{
tok = new StringTokenizer(buf.readLine());
}
catch(Exception e)
{
return false;
}
}
return true;
}
String next()
{
if(hasNext()) return tok.nextToken();
return null;
}
int nextInt()
{
return Integer.parseInt(next());
}
long nextLong()
{
return Long.parseLong(next());
}
double nextDouble()
{
return Double.parseDouble(next());
}
BigInteger nextBigInteger()
{
return new BigInteger(next());
}
BigDecimal nextBigDecimal()
{
return new BigDecimal(next());
}
}
static Queue<Integer> q;
static int n,m,con,start,end;
static int cost,minflow;
static int[]head ,dis, vis,belong_e , pre;
static class e{
int u,v,next,w, cost;
}static e[]es;
static void add(int u,int v,int w, int cost) {
if(es[con]==null) es[con]=new e();
es[con].cost=cost;es[con].v=v;es[con].w=w;es[con].next=head[u];head[u]=con++;
}
//這個本質也是dfs
//就是爲了找一條從源點到匯點的最短路
static boolean spfa() {
for(int i=1;i<=n;i++) {
dis[i]=0x3f3f3f3f;
vis[i]=0;
}
dis[start]=0;vis[start]=1;
q.clear();q.add(start);
while(!q.isEmpty()) {
int u=q.poll();
vis[u]=0;
for(int i=head[u];i!=-1;i=es[i].next) {
int v=es[i].v;
if( es[i].w>0 && dis[v]> dis[u]+es[i].cost ) {
pre[v]=u;belong_e[v]=i;
dis[v]=dis[u]+es[i].cost;
if(vis[v]==0) {
q.add(v);
vis[v]=1;
}
}
}
}
return dis[end]<0x3f3f3f3f;
}
public static void main(String[] args) {
InputReader sc=new InputReader();
head=new int[5005];
belong_e=new int[150000];
pre=new int[5005];
dis=new int[5005];
vis=new int[5005];
es=new e[150005];
q=new LinkedList<Integer>();
n=sc.nextInt();
m=sc.nextInt();
start=sc.nextInt();
end=sc.nextInt();
Arrays.fill(head, -1);con=0;
for(int i=0;i<m;i++) {
int a=sc.nextInt();int b=sc.nextInt();int c=sc.nextInt(); int d=sc.nextInt();
add(a,b,c,d); add(b,a,0,-d);
}
cost=0;
minflow=0;
while(spfa()) {
int temp=0x3f3f3f3f;
for(int i=end;i!=start;i=pre[i]) {
temp=Math.min(temp, es[belong_e[i]].w);
}
cost+=dis[end]*temp;
minflow+=temp;
for(int i=end; i!=start; i=pre[i]) {
es[belong_e[i]].w-=temp;
es[belong_e[i] ^1 ].w+=temp;
}
}
System.out.println(minflow+" "+cost);
}
}