zoukankan      html  css  js  c++  java
  • 最小费用流

    最小费用最大流


    引用自:[最小费用最大流算法](http://blog.csdn.net/jarily/article/details/8613208)

    ### 算法引入: 任何容量网络的最大流流量是唯一且确定的,但是它的最大流f并不是唯一的; 既然最大流f不唯一,因此,如果每条弧上不仅有容量限制,还有费用r; 即每条弧上有一个单位费用的参数,那么在保证最大流的前提下; 还存在一个选择费用最小的最大流问题,即为最小费用最大流问题;
    ###算法思想: 寻找最大流的方法是从某个可行流出发,找到关于这个流的一条增广路P; 沿着P调整f,对新的可行流又试图寻找关于它的增广路,循环直至不存在增广路为止; 要求最小费用最大流: 如果f是流量为f1的可行流中费用最小者,而p是关于f的所有增广路中费用最小的增广路; 那么沿着p去调整f,得到可行流_f,就是流量为f1的所有可行流中的费用最小者; 这样当f是最大流时,它也就是所要求的最小费用最大流了;

    算法内容:

    在寻找关于f的最小费用增广路的过程中;
    需要构造一个关于f的伴随网络W(f);
    把在原网络中寻找关于f的最小费用增广路转换为在伴随网络W(f)中寻找从Vs到Vt的最短路问题;

    其中伴随网络W(f)构造为:
    顶点为原网络中的顶点;
    原网络中的每条弧<u,v>变成两个方向相反的弧<u,v>和<v,u>;
    在W(f)中每条弧<u,v>的权值为:

    if(f(u,v)<c(u,v)) 
        W(u,v)=r(u,v); 
    else if(f(u,v)==c(u,v)) 
        W(u,v)=无穷大(可省略); 
    if(f(u,v)>0) 
        W(v,u)=-r(u,v); 
    else if(f(u,v)==0) 
        W(v,u)=无穷大(可省略); 
    

    算法流程

    算法流程:

    1. 开始取f(0)={0};
    2. 一般若在第k-1步得到的最小费用流为f(k-1),则构造伴随网络W(f(k-1));
    3. 在W(f(k-1))中寻找从Vs到Vt的最短路,若不存在则转⑤,存在转④;
    4. 在原网络G中得到相应的增广路P,在P上对f(k-1)进行调整;调整后新的可行流为f(k),转②;
    5. f(k-1)为最小费用最大流,算法完毕;

    ###我的模板:
    
    
    const int INF = 0x3f3f3f3f;
    const int N = 211, M = N * N;
    
    struct EG {
        int u, v, cap, cost;
    	EG(){}
    	EG(int u, int v, int cap, int cost) : u(u), v(v), cap(cap), cost(cost){}
    } eg[M];
    
    int tot;
    int MaxFlow;
    int first[N], Next[M];
    
    void init()
    {
    	tot = -1;
    	memset(first, -1, sizeof(first));
    }
    void add_edge(int u, int v, int cap, int cost)
    {
    	eg[++tot] = EG(u, v, cap, cost);
    	Next[tot] = first[u]; first[u] = tot;
    	eg[++tot] = EG(v, u, 0, -cost);
    	Next[tot] = first[v]; first[v] = tot;
    }
    
    int dis[N], pre[N];
    bool inq[N];
    bool spfa(int s, int t)
    {
    	memset(dis, 0x3f, sizeof(dis));
    	memset(inq, false, sizeof(inq));
    	queue<int> q;
    	dis[s] = 0;
    	inq[s] = 1;
    	q.push(s);
    	while(!q.empty())
    	{
    		int u = q.front(); q.pop();
    		inq[u] = 0;
    		for(int i=first[u]; i!=-1; i=Next[i])
    		{
    			EG &e = eg[i];
    			if(e.cap && dis[e.v] > dis[u] + e.cost)
    			{
    				dis[e.v] = dis[u] + e.cost;
    				pre[e.v] = i;
    				if(!inq[e.v])
    					q.push(e.v), inq[e.v] = 1;
    			}
    		}
    	}
    	return dis[t] != INF;
    }
    
    int MCMF(int s, int t)
    {
    	int Cost = 0;
    	while(spfa(s,t))
    	{
    		int mn = INF;
    		for(int i=t; i!=s; i=eg[pre[i]].u)
    		{
    			mn = min(mn, eg[pre[i]].cap);
    		}
    		for(int i=t; i!=s; i=eg[pre[i]].u)
    		{
    			eg[pre[i]].cap -= mn;
    			eg[pre[i]^1].cap += mn;
    		}
    		MaxFlow += mn;
    		Cost += mn * dis[t];
    	}
    	return Cost;
    }
    
    

    例题:[hdu-1533](http://acm.hdu.edu.cn/showproblem.php?pid=1533) 题目大意: 给你一个N行M列的矩阵,其中“.”代表空地,“H”代表房子,“m”代表人,其中有n个房子和n个人。现在要求每个人进入一间房子,且人走一步需要支付1美元。 求最小需要花费多少美元才能让所有人都进入到房子中(每个人只能进入一间房子,每个房子只能容纳一个人) 代码: ```c #include #include #include #include #include #include

    using namespace std;

    const int INF = 0x3f3f3f3f;
    const int N = 211, M = N * N;

    struct EG {
    int u, v, cap, cost;
    EG(){}
    EG(int u, int v, int cap, int cost) : u(u), v(v), cap(cap), cost(cost){}
    } eg[M];

    int tot;
    int MaxFlow;
    int first[N], Next[M];

    void init()
    {
    tot = -1;
    memset(first, -1, sizeof(first));
    }
    void add_edge(int u, int v, int cap, int cost)
    {
    eg[++tot] = EG(u, v, cap, cost);
    Next[tot] = first[u]; first[u] = tot;
    eg[++tot] = EG(v, u, 0, -cost);
    Next[tot] = first[v]; first[v] = tot;
    }

    int dis[N], pre[N];
    bool inq[N];
    bool spfa(int s, int t)
    {
    memset(dis, 0x3f, sizeof(dis));
    memset(inq, false, sizeof(inq));
    queue q;
    dis[s] = 0;
    inq[s] = 1;
    q.push(s);
    while(!q.empty())
    {
    int u = q.front(); q.pop();
    inq[u] = 0;
    for(int i=first[u]; i!=-1; i=Next[i])
    {
    EG &e = eg[i];
    if(e.cap && dis[e.v] > dis[u] + e.cost)
    {
    dis[e.v] = dis[u] + e.cost;
    pre[e.v] = i;
    if(!inq[e.v])
    q.push(e.v), inq[e.v] = 1;
    }
    }
    }
    return dis[t] != INF;
    }

    int MCMF(int s, int t)
    {
    int Cost = 0;
    while(spfa(s,t))
    {
    int mn = INF;
    for(int i=t; i!=s; i=eg[pre[i]].u)
    {
    mn = min(mn, eg[pre[i]].cap);
    }
    for(int i=t; i!=s; i=eg[pre[i]].u)
    {
    eg[pre[i]].cap -= mn;
    eg[pre[i]^1].cap += mn;
    }
    MaxFlow += mn;
    Cost += mn * dis[t];
    }
    return Cost;
    }
    char s[N];
    pair<int,int> a[N], b[N];
    int main()
    {
    int n, m;
    while(scanf("%d %d", &n, &m) == 2 && (n+m))
    {
    int nx = 0, ny = 0;
    for(int i=1; i<=n; i++)
    {
    scanf("%s", s+1);
    for(int j=1; j<=m; j++)
    {
    if(s[j] == 'H')
    a[++nx] = make_pair(i, j);
    if(s[j] == 'm')
    b[++ny] = make_pair(i, j);
    }
    }
    init();
    int s = 0, t = nx + ny + 1;
    for(int i=1; i<=nx; i++)
    {
    add_edge(s, i, 1, 0);
    for(int j=1; j<=ny; j++)
    {
    add_edge(i, nx+j, 1, abs(a[i].first-b[j].first)+abs(a[i].second-b[j].second));
    }
    }
    for(int i=1; i<=ny; i++)
    {
    add_edge(nx+i, t, 1, 0);
    }
    printf("%d ", MCMF(s, t));
    }
    return 0;
    }

    </br>
    [小结练习](http://acm.hust.edu.cn/vjudge/contest/view.action?cid=89465#overview)
    </br>
  • 相关阅读:
    利用Unicorn和Idaemu辅助解决Geekpwn SecretCode
    2016-XCTF Final-Richman
    一道movfuscator混淆过的简单逆向
    airflow(二)集成EMR使用
    Airflow 调度基础
    集成学习与随机森林(二)Bagging与Pasting
    集成学习与随机森林(一)投票分类器
    决策树(二)决策树回归
    决策树(一)决策树分类
    SVM-支持向量机(三)SVM回归与原理
  • 原文地址:https://www.cnblogs.com/WCB-ACM/p/5259553.html
Copyright © 2011-2022 走看看