SorryToPerson logo
返回
算法2026-04-23·9 分钟

算法知识库:网络流算法实现

JavaScript/TypeScript 实现网络流算法,如最大流、最小割等。

网络流算法实现

1. 最大流 (Ford-Fulkerson 算法)

ts
class FlowNetwork {
  private graph: number[][];
  private residualGraph: number[][];
  private vertices: number;

  constructor(vertices: number) {
    this.vertices = vertices;
    this.graph = Array.from({ length: vertices }, () => Array(vertices).fill(0));
    this.residualGraph = Array.from({ length: vertices }, () => Array(vertices).fill(0));
  }

  addEdge(from: number, to: number, capacity: number): void {
    this.graph[from][to] = capacity;
    this.residualGraph[from][to] = capacity;
    this.residualGraph[to][from] = 0;
  }

  fordFulkerson(source: number, sink: number): number {
    let maxFlow = 0;
    const parent = new Array(this.vertices).fill(-1);

    while (this.bfs(source, sink, parent)) {
      let pathFlow = Infinity;

      for (let v = sink; v !== source; v = parent[v]) {
        const u = parent[v];
        pathFlow = Math.min(pathFlow, this.residualGraph[u][v]);
      }

      for (let v = sink; v !== source; v = parent[v]) {
        const u = parent[v];
        this.residualGraph[u][v] -= pathFlow;
        this.residualGraph[v][u] += pathFlow;
      }

      maxFlow += pathFlow;
    }

    return maxFlow;
  }

  private bfs(source: number, sink: number, parent: number[]): boolean {
    const visited = new Array(this.vertices).fill(false);
    const queue: number[] = [];

    queue.push(source);
    visited[source] = true;
    parent[source] = -1;

    while (queue.length > 0) {
      const u = queue.shift()!;

      for (let v = 0; v < this.vertices; v += 1) {
        if (!visited[v] && this.residualGraph[u][v] > 0) {
          queue.push(v);
          parent[v] = u;
          visited[v] = true;
        }
      }
    }

    return visited[sink];
  }
}

2. 最小割

ts
function minCut(graph: number[][], source: number, sink: number): { cutValue: number; cut: [number, number][] } {
  const network = new FlowNetwork(graph.length);

  for (let i = 0; i < graph.length; i += 1) {
    for (let j = 0; j < graph[i].length; j += 1) {
      if (graph[i][j] > 0) {
        network.addEdge(i, j, graph[i][j]);
      }
    }
  }

  const maxFlow = network.fordFulkerson(source, sink);

  // 使用 BFS 找到可达顶点
  const visited = new Array(graph.length).fill(false);
  const queue: number[] = [];
  queue.push(source);
  visited[source] = true;

  while (queue.length > 0) {
    const u = queue.shift()!;

    for (let v = 0; v < graph.length; v += 1) {
      if (!visited[v] && network.getResidualCapacity(u, v) > 0) {
        queue.push(v);
        visited[v] = true;
      }
    }
  }

  // 找到割边
  const cut: [number, number][] = [];
  for (let i = 0; i < graph.length; i += 1) {
    for (let j = 0; j < graph.length; j += 1) {
      if (visited[i] && !visited[j] && graph[i][j] > 0) {
        cut.push([i, j]);
      }
    }
  }

  return { cutValue: maxFlow, cut };
}

// 为 FlowNetwork 添加获取残余容量的方法
declare module './flow-network' {
  interface FlowNetwork {
    getResidualCapacity(u: number, v: number): number;
  }
}

FlowNetwork.prototype.getResidualCapacity = function (u: number, v: number): number {
  return this.residualGraph[u][v];
};

3. 最大匹配 (二分图匹配)

ts
function bipartiteMatching(graph: number[][]): number {
  const n = graph.length;
  const m = graph[0].length;
  const matchR = new Array(m).fill(-1);
  const seen = new Array(m).fill(false);

  let result = 0;

  for (let u = 0; u < n; u += 1) {
    seen.fill(false);
    if (bpm(graph, u, matchR, seen)) {
      result += 1;
    }
  }

  return result;
}

function bpm(graph: number[][], u: number, matchR: number[], seen: boolean[]): boolean {
  for (let v = 0; v < graph[u].length; v += 1) {
    if (graph[u][v] && !seen[v]) {
      seen[v] = true;

      if (matchR[v] < 0 || bpm(graph, matchR[v], matchR, seen)) {
        matchR[v] = u;
        return true;
      }
    }
  }

  return false;
}

4. 最小费用最大流

ts
interface Edge {
  to: number;
  capacity: number;
  cost: number;
  reverse: number;
}

class MinCostFlow {
  private graph: Edge[][][];
  private vertices: number;

  constructor(vertices: number) {
    this.vertices = vertices;
    this.graph = Array.from({ length: vertices }, () => []);
  }

  addEdge(from: number, to: number, capacity: number, cost: number): void {
    this.graph[from].push({
      to,
      capacity,
      cost,
      reverse: this.graph[to].length,
    });
    this.graph[to].push({
      to: from,
      capacity: 0,
      cost: -cost,
      reverse: this.graph[from].length - 1,
    });
  }

  minCostFlow(source: number, sink: number, flow: number): number {
    let totalCost = 0;
    const INF = Infinity;

    while (flow > 0) {
      const dist = new Array(this.vertices).fill(INF);
      const prevv = new Array(this.vertices).fill(0);
      const preve = new Array(this.vertices).fill(0);

      dist[source] = 0;

      // Bellman-Ford
      let update = true;
      while (update) {
        update = false;
        for (let v = 0; v < this.vertices; v += 1) {
          if (dist[v] === INF) continue;
          for (let i = 0; i < this.graph[v].length; i += 1) {
            const edge = this.graph[v][i];
            if (edge.capacity > 0 && dist[edge.to] > dist[v] + edge.cost) {
              dist[edge.to] = dist[v] + edge.cost;
              prevv[edge.to] = v;
              preve[edge.to] = i;
              update = true;
            }
          }
        }
      }

      if (dist[sink] === INF) break;

      let d = flow;
      for (let v = sink; v !== source; v = prevv[v]) {
        d = Math.min(d, this.graph[prevv[v]][preve[v]].capacity);
      }

      flow -= d;
      totalCost += d * dist[sink];

      for (let v = sink; v !== source; v = prevv[v]) {
        const edge = this.graph[prevv[v]][preve[v]];
        edge.capacity -= d;
        this.graph[v][edge.reverse].capacity += d;
      }
    }

    return totalCost;
  }
}

5. 实现要点

  • Ford-Fulkerson 使用 BFS 寻找增广路径。
  • 最小割与最大流对偶。
  • 二分图匹配使用 DFS 寻找交替路径。
  • 最小费用流结合最短路径和最大流。
算法网络流JavaScript