洛谷P3381 【模板】最小费用最大流

题目链接:https://www.luogu.com.cn/problem/P3381

代码实现

  1. import java.io.*;
  2. import java.util.*;
  3. public class Main {
  4. // 图
  5. private static List<List<Edge>> graph;
  6. // 顶点数
  7. static int V;
  8. // 顶点的势
  9. static int[] h;
  10. // 到达目标结点的最短距离
  11. static int[] dist;
  12. // 最短路中的前驱节点
  13. static int[] prevV;
  14. // 最短路中的前驱节点对应的边
  15. static int[] prevE;
  16. public static void main(String[] args) throws IOException {
  17. StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
  18. PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
  19. in.nextToken();
  20. int N = (int) in.nval;
  21. in.nextToken();
  22. int M = (int) in.nval;
  23. in.nextToken();
  24. int src = (int) in.nval;
  25. in.nextToken();
  26. int dst = (int) in.nval;
  27. V = N + 1;
  28. init();
  29. while (M-- > 0) {
  30. int from, to, capacity, cost;
  31. in.nextToken();
  32. from = (int) in.nval;
  33. in.nextToken();
  34. to = (int) in.nval;
  35. in.nextToken();
  36. capacity = (int) in.nval;
  37. in.nextToken();
  38. cost = (int) in.nval;
  39. addEgde(from, to, capacity, cost);
  40. }
  41. int[] ans = minCostFlow(src, dst);
  42. out.println(ans[0] + " " + ans[1]);
  43. out.flush();
  44. }
  45. private static void init() {
  46. graph = new ArrayList<List<Edge>>(V);
  47. for (int i = 0; i < V; ++i) {
  48. graph.add(new ArrayList<Edge>());
  49. }
  50. h = new int[V];
  51. dist = new int[V];
  52. prevV = new int[V];
  53. prevE = new int[V];
  54. }
  55. // 向图中增加一条从from到to的容量为cap费用为cost的边
  56. private static void addEgde(int from, int to, int cap, int cost) {
  57. graph.get(from).add(new Edge(to, cap, cost, graph.get(to).size()));
  58. graph.get(to).add(new Edge(from, 0, -cost, graph.get(from).size() - 1));
  59. }
  60. // 求解从s到t流量为f的最大容量最小费用流
  61. private static int[] minCostFlow(final int src, final int dst) {
  62. int res = 0;
  63. int flow = 0;
  64. Arrays.fill(h, 0);
  65. while (true) {
  66. // 使用Dijkstra算法更新h
  67. Arrays.fill(dist, Integer.MAX_VALUE);
  68. dist[src] = 0;
  69. // int[0]存储最短距离
  70. // int[1]存储结点编号
  71. // 建立小根堆
  72. Comparator<int[]> comparator = new Comparator<int[]>() {
  73. public int compare(int[] o1, int[] o2) {
  74. return o1[0] - o2[0];
  75. }
  76. };
  77. Queue<int[]> queue = new PriorityQueue<int[]>(comparator);
  78. queue.offer(new int[]{0, src});
  79. while (!queue.isEmpty()) {
  80. int[] pair = queue.poll();
  81. int v = pair[1];
  82. if (dist[v] < pair[0]) {
  83. continue;
  84. }
  85. for (int i = 0; i < graph.get(v).size(); ++i) {
  86. Edge edge = graph.get(v).get(i);
  87. if ((edge.capacity > 0) && (dist[edge.to] > dist[v] + edge.cost + h[v] - h[edge.to])) {
  88. dist[edge.to] = dist[v] + edge.cost + h[v] - h[edge.to];
  89. prevV[edge.to] = v;
  90. prevE[edge.to] = i;
  91. queue.offer(new int[]{dist[edge.to], edge.to});
  92. }
  93. }
  94. }
  95. // 不能再增广
  96. if (dist[dst] == Integer.MAX_VALUE) {
  97. break;
  98. }
  99. for (int v = 0; v < V; ++v) {
  100. h[v] += dist[v];
  101. }
  102. // 找到路径上容量最小的边
  103. int minCapacity = Integer.MAX_VALUE;
  104. for (int v = dst; v != src; v = prevV[v]) {
  105. minCapacity = Math.min(minCapacity, graph.get(prevV[v]).get(prevE[v]).capacity);
  106. }
  107. // 沿s到t的最短路尽量增广
  108. // 计算残差网络
  109. flow += minCapacity;
  110. res += minCapacity * h[dst];
  111. for (int v = dst; v != src; v = prevV[v]) {
  112. Edge e = graph.get(prevV[v]).get(prevE[v]);
  113. e.capacity -= minCapacity;
  114. graph.get(v).get(e.reserve).capacity += minCapacity;
  115. }
  116. }
  117. return new int[]{flow, res};
  118. }
  119. }
  120. class Edge {
  121. int to;
  122. int capacity;
  123. int cost;
  124. int reserve;
  125. public Edge(int to, int capacity, int cost, int reserve) {
  126. this.to = to;
  127. this.capacity = capacity;
  128. this.cost = cost;
  129. this.reserve = reserve;
  130. }
  131. }

输入样例

  1. 4 5 4 3
  2. 4 2 30 2
  3. 4 3 20 3
  4. 2 3 20 1
  5. 2 1 30 9
  6. 1 3 40 5

输出样例

  1. 50 280

可行流最小费用

根据模板简单修改。

代码实现

  1. import java.io.*;
  2. import java.util.*;
  3. public class Main {
  4. // 图
  5. private static List<List<Edge>> graph;
  6. // 顶点数
  7. static int V;
  8. // 顶点的势
  9. static int[] h;
  10. // 到达目标结点的最短距离
  11. static int[] dist;
  12. // 最短路中的前驱节点
  13. static int[] prevV;
  14. // 最短路中的前驱节点对应的边
  15. static int[] prevE;
  16. public static void main(String[] args) throws IOException {
  17. StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
  18. PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
  19. in.nextToken();
  20. int N = (int) in.nval;
  21. in.nextToken();
  22. int M = (int) in.nval;
  23. in.nextToken();
  24. int src = (int) in.nval;
  25. in.nextToken();
  26. int dst = (int) in.nval;
  27. V = N + 1;
  28. init();
  29. while (M-- > 0) {
  30. int from, to, capacity, cost;
  31. in.nextToken();
  32. from = (int) in.nval;
  33. in.nextToken();
  34. to = (int) in.nval;
  35. in.nextToken();
  36. capacity = (int) in.nval;
  37. in.nextToken();
  38. cost = (int) in.nval;
  39. addEgde(from, to, capacity, cost);
  40. }
  41. int ans = minCostFlow(src, dst, 50);
  42. out.println(ans);
  43. out.flush();
  44. }
  45. private static void init() {
  46. graph = new ArrayList<List<Edge>>(V);
  47. for (int i = 0; i < V; ++i) {
  48. graph.add(new ArrayList<Edge>());
  49. }
  50. h = new int[V];
  51. dist = new int[V];
  52. prevV = new int[V];
  53. prevE = new int[V];
  54. }
  55. // 向图中增加一条从from到to的容量为cap费用为cost的边
  56. private static void addEgde(int from, int to, int cap, int cost) {
  57. graph.get(from).add(new Edge(to, cap, cost, graph.get(to).size()));
  58. graph.get(to).add(new Edge(from, 0, -cost, graph.get(from).size() - 1));
  59. }
  60. // 求解从s到t流量为f的最小费用流,如果没有流量为f的流,则返回-1
  61. private static int minCostFlow(final int src, final int dst, int flow) {
  62. int res = 0;
  63. Arrays.fill(h, 0);
  64. while (flow > 0) {
  65. // 使用Dijkstra算法更新h
  66. Arrays.fill(dist, Integer.MAX_VALUE);
  67. dist[src] = 0;
  68. // int[0]存储最短距离
  69. // int[1]存储结点编号
  70. // 建立小根堆
  71. Comparator<int[]> comparator = new Comparator<int[]>() {
  72. public int compare(int[] o1, int[] o2) {
  73. return o1[0] - o2[0];
  74. }
  75. };
  76. Queue<int[]> queue = new PriorityQueue<int[]>(comparator);
  77. queue.offer(new int[]{0, src});
  78. while (!queue.isEmpty()) {
  79. int[] pair = queue.poll();
  80. int v = pair[1];
  81. if (dist[v] < pair[0]) {
  82. continue;
  83. }
  84. for (int i = 0; i < graph.get(v).size(); ++i) {
  85. Edge edge = graph.get(v).get(i);
  86. if ((edge.capacity > 0) && (dist[edge.to] > dist[v] + edge.cost + h[v] - h[edge.to])) {
  87. dist[edge.to] = dist[v] + edge.cost + h[v] - h[edge.to];
  88. prevV[edge.to] = v;
  89. prevE[edge.to] = i;
  90. queue.offer(new int[]{dist[edge.to], edge.to});
  91. }
  92. }
  93. }
  94. // 不能再增广
  95. if (dist[dst] == Integer.MAX_VALUE) {
  96. return -1;
  97. }
  98. for (int v = 0; v < V; ++v) {
  99. h[v] += dist[v];
  100. }
  101. // 找到路径上容量最小的边
  102. int minCapacity = Integer.MAX_VALUE;
  103. for (int v = dst; v != src; v = prevV[v]) {
  104. minCapacity = Math.min(minCapacity, graph.get(prevV[v]).get(prevE[v]).capacity);
  105. }
  106. // 沿s到t的最短路尽量增广
  107. // 计算残差网络
  108. flow -= minCapacity;
  109. res += minCapacity * h[dst];
  110. for (int v = dst; v != src; v = prevV[v]) {
  111. Edge e = graph.get(prevV[v]).get(prevE[v]);
  112. e.capacity -= minCapacity;
  113. graph.get(v).get(e.reserve).capacity += minCapacity;
  114. }
  115. }
  116. return res;
  117. }
  118. }
  119. class Edge {
  120. int to;
  121. int capacity;
  122. int cost;
  123. int reserve;
  124. public Edge(int to, int capacity, int cost, int reserve) {
  125. this.to = to;
  126. this.capacity = capacity;
  127. this.cost = cost;
  128. this.reserve = reserve;
  129. }
  130. }

样例输入

  1. 4 5 4 3
  2. 4 2 30 2
  3. 4 3 20 3
  4. 2 3 20 1
  5. 2 1 30 9
  6. 1 3 40 5

样例输出

  1. 280