最小费用最大流

POJ2195

建图,直接套用模板

求出每个人到每个房子最短的距离来构造费用(cost),因为一个房子只能容纳一个人,所以容量(cap)都为1,然后建立超级源点与所有的人相连,所有的房子与超级汇点相连就行了。显然,超级源点超级汇点的费用都是0;

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
#include <queue>
#define mem(a,x) memset(a,x,sizeof(a))
#define INF 0x3f3f3f3f
#define N 222
#define M 111*100
typedef long long ll;
using namespace std;
struct Edge{
    int from,to,cap,flow,cost;
};
struct MCMF{
    int n,m,s,t;
    vector<Edge> edges;
    vector<int> G[N];
    int inq[N]; //是否在队列中
    int d[N]; //bellman_ford;
    int p[N]; //上一条弧
    int a[N]; //可改进量
    void init(int n){
        this->n = n;
        for(int i=0;i<=n;i++) G[i].clear();
        edges.clear();
    }
    void addEdge(int from,int to,int cap,int cost){
        edges.push_back((Edge){from,to,cap,0,cost});
        edges.push_back((Edge){to,from,0,0,-cost});
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }
    bool BellmanFord(int s,int t,int &flow,int &cost){
        for(int i=0;i<=n;i++) d[i] = INF;
        mem(inq,0);
        d[s] = 0;inq[s] = 1;p[s] =0;a[s] = INF;

        queue<int>Q;
        Q.push(s);
        while(!Q.empty()){
            int u = Q.front(); Q.pop();
            inq[u] = 0;
            for(int i=0;i<G[u].size();i++){
                Edge& e = edges[G[u][i]];
                if(e.cap >e.flow && d[e.to] > d[u] + e.cost){
                    d[e.to] = d[u] + e.cost;
                    p[e.to] = G[u][i];
                    a[e.to] = min(a[u] , e.cap - e.flow);
                    if(!inq[e.to]){ Q.push(e.to); inq[e.to] = 1;}
                }
            }
        }
        if(d[t] == INF) return false; // s-t不连通,失败退出
        flow += a[t];
        cost += d[t]*a[t];
        for(int u=t;u!=s;u=edges[p[u]].from){
            edges[p[u]].flow += a[t];
            edges[p[u]^1].flow -= a[t];
        }
        return true;
    }
    //需要保证初始网络中没有负圈
    int minCost(int s,int t){
        int flow = 0,cost = 0;
        while(BellmanFord(s,t,flow,cost));
        return cost;
    }
}solve;
char map[101][101];
struct node{
    int x,y;
}house[N],man[N];
int main(){
    int n,m;while(scanf("%d%d",&n,&m),n+m){
        for(int i=0;i<n;i++)scanf("%s",map[i]);
        int l = 0,r = 0;
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(map[i][j] == 'H')
                    house[l++] = node{i,j};
                else if(map[i][j] == 'm')
                    man[r++] = node{i,j};
            }
        }
        int src = r + l; int des = r + l+1 ;
        solve.init(r+l+1);
        for(int i=0;i<r;i++){
            for(int j=0;j<l;j++){
                int cost = abs(man[i].x - house[j].x) + abs(man[i].y - house[j].y);
                solve.addEdge(i,r+j,1,cost);
            }
        }
        for(int i=0;i<r;i++) solve.addEdge(src,i,1,0);
        for(int i=0;i<l;i++) solve.addEdge(i+r,des,1,0);
        int ans = solve.minCost(src,des);
        printf("%d\n",ans);
    }

}

POJ2135神构图

要求一个人从位置1走到位置n,然后再走回位置1,走的路不能重复,要求路径最短。

思路:

每条路正反都要建立一次图,容量都为1,这样保证了只走一次;

建立超级源点,超级汇点,因为位置1,位置n要走两次(题意要求来回一次)。

所以超级源点的到位置1的 容量(cap)为2,cost为0(显然)

位置n到超级汇点的cap为2,cost为0;

然后直接求 超级源点到超级汇点的 最小费用最大流就行了。


#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
#include <queue>
#define mem(a,x) memset(a,x,sizeof(a))
#define INF 0x3f3f3f3f
#define N 1110
#define M 111*100
typedef long long ll;
using namespace std;
struct Edge{
    int from,to,cap,flow,cost;
};
struct MCMF{
    int n,m,s,t;
    vector<Edge> edges;
    vector<int> G[N];
    int inq[N]; //是否在队列中
    int d[N]; //bellman_ford;
    int p[N]; //上一条弧
    int a[N]; //可改进量
    void init(int n){
        this->n = n;
        for(int i=0;i<=n;i++) G[i].clear();
        edges.clear();
    }
    void addEdge(int from,int to,int cap,int cost){
        edges.push_back((Edge){from,to,cap,0,cost});
        edges.push_back((Edge){to,from,0,0,-cost});
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }
    bool BellmanFord(int s,int t,int &flow,int &cost){
        for(int i=0;i<=n+1;i++) d[i] = INF;
        mem(inq,0);
        d[s] = 0;inq[s] = 1;p[s] =0;a[s] = INF;

        queue<int>Q;
        Q.push(s);
        while(!Q.empty()){
            int u = Q.front(); Q.pop();
            inq[u] = 0;
            for(int i=0;i<G[u].size();i++){
                Edge& e = edges[G[u][i]];
                if(e.cap >e.flow && d[e.to] > d[u] + e.cost){
                    d[e.to] = d[u] + e.cost;
                    p[e.to] = G[u][i];
                    a[e.to] = min(a[u] , e.cap - e.flow);
                    if(!inq[e.to]){ Q.push(e.to); inq[e.to] = 1;}
                }
            }
        }
        if(d[t] == INF) return false; // s-t不连通,失败退出
        flow += a[t];
        cost += d[t]*a[t];
        int u = t;
        while(u != s){
            edges[p[u]].flow += a[t];
            edges[p[u]^1].flow -= a[t];
            u = edges[p[u]].from;
        }
         return true;
    }
    int minCost(int s,int t){
        int flow = 0,cost = 0;
        while(BellmanFord(s,t,flow,cost));
        return cost;
    }
}solve;
int main(){
    int n,m;while(scanf("%d%d",&n,&m)!=EOF){
        int a,b,c;
        solve.init(n);
        for(int i=0;i<m;i++){
            scanf("%d%d%d",&a,&b,&c);
            solve.addEdge(a,b,1,c);
            solve.addEdge(b,a,1,c);
        }
        solve.addEdge(0,1,2,0);
        solve.addEdge(n,n+1,2,0);
        int ans = solve.minCost(0,n+1);
        printf("%d\n",ans);
    }

}

模板还是比较可靠的 - -

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值