算法笔记_140:最小费用最大流问题(Java)

目录

1 问题描述

2 解决方案

 


1 问题描述

在最大流有多组解时,给每条边在附上一个单位费用的量,问在满足最大流时的最小费用是多少?

 

 


2 解决方案

下面代码所使用的测试数据如下图:

 

具体代码如下:

package com.liuzhen.practice;

import java.util.ArrayList;
import java.util.Scanner;

public class Main {
    public static int MAX = 1000;
    public static int n;   //图中顶点数目
    public static boolean[] used = new boolean[MAX];   //判断顶点是否在队列中
    public static int[] pre = new int[MAX];   //记录最短增广路径中相应节点的前节点
    public static int[] distance = new int[MAX];   //记录源点到图中其他所有顶点的最短距离
    public static int[] capacity = new int[MAX];  //用于记录遍历图每一次得到增广路径的流量
    public static ArrayList<edge>[] map;   //图的邻接表
    //表示图中边信息内部类
    static class edge {
        public int from;   //边的起点
        public int to;     //边的终点
        public int cap;    //边的容量
        public int cost;   //边的费用
        
        public edge(int from, int to, int cap, int cost) {
            this.from = from;
            this.to = to;
            this.cap = cap;
            this.cost = cost;
        }
    }
    //输入给定图数据
    @SuppressWarnings("unchecked")
    public void init() {
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        int k = in.nextInt();  //给定图的边数目
        map = new ArrayList[n];
        for(int i = 0;i < n;i++)
            map[i] = new ArrayList<edge>();
        for(int i = 0;i < k;i++) {
            int from = in.nextInt();
            int to = in.nextInt();
            int cap = in.nextInt();
            int cost = in.nextInt();
            map[from].add(new edge(from, to, cap, cost));  //正向边
            map[to].add(new edge(to, from, 0, -cost));     //反向边
        }
    }
    
    //寻找顶点start到顶点end的最短路径(PS:即费用最少的一条增广路径)
    public boolean spfa(int start, int end) {  
        int[] count = new int[n];
        for(int i = 0;i < n;i++) {
            used[i] = false;
            pre[i] = -1;
            distance[i] = Integer.MAX_VALUE;
            capacity[i] = Integer.MAX_VALUE;
        }
        used[start] = true;
        pre[start] = start;
        distance[start] = 0;
        count[start]++;
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(start);
        while(!list.isEmpty()) {
            int index = list.get(0);
            list.remove(0);
            used[index] = false;
            for(int i = 0;i < map[index].size();i++) {
                edge temp = map[index].get(i);
                if(temp.cap > 0 && distance[temp.to] > distance[index] + temp.cost) {
                    //记录顶点start到图中其它顶点之间的最短费用距离
                    distance[temp.to] = distance[index] + temp.cost;
                    pre[temp.to] = index;
                    //记录增广路径能够流通的最大流量
                    capacity[temp.to] = Math.min(capacity[index], temp.cap);
                    if(!used[temp.to]) {
                        used[temp.to] = true;
                        list.add(temp.to);
                        count[temp.to]++;
                        if(count[temp.to] > n)   //用于判断图中是否有负环
                            return false;
                    }
                }
            }
        }
        if(distance[end] != Integer.MAX_VALUE && pre[end] != -1)
            return true;
        return false;
    }
    
    public int getResult() {
        init();   //输入给定图数据
        int minCost = 0;
        int start = 0;   //把源点设置为顶点0
        int end = n - 1;  //把汇点设置为顶点n - 1
        while(true) {
            if(spfa(start, end) == false)
                break;
            System.out.println("增广路径增量:"+capacity[end]+", 费用流:"+distance[end]);
            minCost += distance[end] * capacity[end];
            int last = end;
            int begin = end;
            System.out.print("汇点出发");
            while(begin != start) {
                last = begin;
                begin = pre[last];
                int i = 0, j = 0;
                System.out.print("——>"+last);
                for(;i < map[begin].size();i++) {
                    if(map[begin].get(i).to == last)
                        break;
                }
                map[begin].get(i).cap -= capacity[end];  //正向边剩余流量减少
                for(;j < map[last].size();j++) {
                    if(map[last].get(j).to == begin)
                        break;
                }
                map[last].get(j).cap += capacity[end];  //反向边剩余流量增加
            }    
            System.out.println("——>"+begin);
        }
        return minCost;
    }
    
    public static void main(String[] args) {
        Main test = new Main();
        int result = test.getResult();
        System.out.println(result);
    }
}

 

运行结果:

6
7
0 1 2 1
0 3 3 2
1 2 5 5
1 4 3 4
2 5 2 10
3 2 1 3
4 5 4 7
增广路径增量:2, 费用流:12
汇点出发——>5——>4——>1——>0
增广路径增量:1, 费用流:15
汇点出发——>5——>2——>3——>0
39

 

 

 

 

参考资料:

   1. 最小费用最大流详解与模板

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值