其他分享
首页 > 其他分享> > 【最小费用最大流】Going Home

【最小费用最大流】Going Home

作者:互联网

概念:

在同一个网络中,可能存在多个总流量相同的最大流,我们可以在计算流量的基础之上,给网络中的弧增加一个单位流量的费用(简称费用),在确保流量最大的前提下总费用最小——最小费用最大流。
在这里插入图片描述
C - Going Home

On a grid map there are n little men and n houses. In each unit time, every little man can move one unit step, either horizontally, or vertically, to an adjacent point. For each little man, you need to pay a $1 travel fee for every step he moves, until he enters a house. The task is complicated with the restriction that each house can accommodate only one little man.

Your task is to compute the minimum amount of money you need to pay in order to send these n little men into those n different houses. The input is a map of the scenario, a ‘.’ means an empty space, an ‘H’ represents a house on that point, and am ‘m’ indicates there is a little man on that point.
在这里插入图片描述
You can think of each point on the grid map as a quite large square, so it can hold n little men at the same time; also, it is okay if a little man steps on a grid with a house without entering that house.
Input
There are one or more test cases in the input. Each case starts with a line giving two integers N and M, where N is the number of rows of the map, and M is the number of columns. The rest of the input will be N lines describing the map. You may assume both N and M are between 2 and 100, inclusive. There will be the same number of 'H’s and 'm’s on the map; and there will be at most 100 houses. Input will terminate with 0 0 for N and M.
Output
For each test case, output one line with the single integer, which is the minimum amount, in dollars, you need to pay.

Sample Input
2 2
.m
H.
5 5
HH..m
.....
.....
.....
mm..H
7 8
...H....
...H....
...H....
mmmHmmmm
...H....
...H....
...H....
0 0
Sample Output
2
10
28

在这里插入图片描述

建图:1到n为人的编号,n+1到2n为房子的编号,另加上源点0和汇点2n+1;
源点到每个人各建立一条容量为1的流,费用为0;
每个人到每个房子各建立一条容量为1的流,费用按题意计算;(注意:反向费用!!!)
每个房子到汇点建立一条容量为1的流,费用为0。
当满足最大流时,一定是源点发出n,每个人接收1并发出1到一个房子,n个房子各发出1汇成n到汇点

这题最小费用就是最短距离。
建图的过程:
超级源点连接人,容量为1,代价为0;
人连接房屋,容量为1,代价为曼哈顿距离(最短距离);
房屋连接超级汇点,容量为1,代价为0。

建图是原点到人 ,人到屋子, 屋子到汇点。流量均为1,人与屋子费用为距离。其他费用为0;
预处理出每个人到每间房的距离


#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;


//************************************************************
//最小费用最大流算法
//SPFA求最短路
//邻接矩阵形式
//初始化:cap:容量,没有边为0
//cost:耗费,对称形式,没有边的也为0
//c是最小费用
//f是最大流
//*******************************************************
const int MAXN=500;
const int INF=0x3fffffff;
int cap[MAXN][MAXN];//容量,没有边为0
int flow[MAXN][MAXN];
//耗费矩阵是对称的,有i到j的费用,则j到i的费用为其相反数
int cost[MAXN][MAXN];//花费


int n;//顶点数目0~n-1
int f;//最大流
int c;//最小费用
int start,end;//源点和汇点

bool vis[MAXN];//在队列标志
int que[MAXN];
int pre[MAXN];
int dist[MAXN];//s-t路径最小耗费
bool SPFA()
{
    int front=0,rear=0;
    for(int u=0;u<=n;u++)
    {
        if(u==start)
        {
            que[rear++]=u;
            dist[u]=0;
            vis[u]=true;
        }
        else
        {
            dist[u]=INF;
            vis[u]=false;
        }
    }
    while(front!=rear)
    {
        int u=que[front++];
        vis[u]=false;
        if(front>=MAXN)front=0;
        for(int v=0;v<=n;v++)
        {
            if(cap[u][v]>flow[u][v]&&dist[v]>dist[u]+cost[u][v])
            {
                dist[v]=dist[u]+cost[u][v];
                pre[v]=u;
                if(!vis[v])
                {
                    vis[v]=true;
                    que[rear++]=v;
                    if(rear>=MAXN)rear=0;
                }
            }
        }
    }
    if(dist[end]>=INF)return false;
    return true;
}

void minCostMaxflow()
{
    memset(flow,0,sizeof(flow));
    c=f=0;
    while(SPFA())
    {
        int Min=INF;
        for(int u=end;u!=start;u=pre[u])
           Min=min(Min,cap[pre[u]][u]-flow[pre[u]][u]);
        for(int u=end;u!=start;u=pre[u])
        {
            flow[pre[u]][u]+=Min;
            flow[u][pre[u]]-=Min;
        }
        c+=dist[end]*Min;
        f+=Min;
    }
}
//************************************************************

struct Node
{
    int x,y;
};

Node node1[MAXN],node2[MAXN];
char str[MAXN][MAXN];

int main()
{
    int N,M;
    while(~scanf("%d%d",&N,&M))
    {
        if(N==0&&M==0)break;

        int tol1=0,tol2=0;
        //人和房子的数目 从 1 开始

        for(int i=0;i<N;i++)
        {
            scanf("%s",&str[i]);

            for(int j=0;j<M;j++)
            {
                if(str[i][j]=='m')
                {
                    tol1++;
                    node1[tol1].x=i;
                    node1[tol1].y=j;
                }
                else if(str[i][j]=='H')
                {
                    tol2++;
                    node2[tol2].x=i;
                    node2[tol2].y=j;
                }
            }
        }

        start=0;

        n=tol1+tol2+1;

        end=tol1+tol2+1;

        memset(cap,0,sizeof(cap));
        memset(cost,0,sizeof(cost));

        for(int i=1;i<=tol1;i++)
        {
            cost[0][i]=cost[i][0]=0;
            cap[0][i]=1;
        }

        for(int i=1;i<=tol2;i++)
        {
            cost[tol1+i][end]=0;
            cap[tol1+i][end]=1;
        }

        for(int i=1;i<=tol1;i++)
          for(int j=1;j<=tol2;j++)
          {
              cost[i][tol1+j]=abs(node1[i].x-node2[j].x)+abs(node1[i].y-node2[j].y);

              cost[tol1+j][i]=-cost[i][tol1+j];

              cap[i][tol1+j]=1;
          }

        minCostMaxflow();
        printf("%d\n",c);
    }
    return 0;
}

#include <cstdio>
#include <cmath>
#include <cstring>
#include <queue>
#include <algorithm>
 
using namespace std;
 
const int maxn = 2 * (100 + 10);
const int INF = 0x3f3f3f3f;
 
struct node{        //结点类型
    int x;
    int y;
}m[maxn], h[maxn];
 
int N, M, n, t, mid, hid, cost[maxn][maxn], cap[maxn][maxn], flow[maxn][maxn], p[maxn];
vector<node> man, house;
 
void init(){        //初始化
    mid = 0;
    hid = 0;
    memset(cost, 0, sizeof(cost));
    memset(cap, 0, sizeof(cap));
}
 
void build(){       //建图
    n = mid;
    t = mid + hid + 1;
    int i, j;
    for(i = 1; i <= n; i++){
        for(j = 1; j <= n; j++){
            cap[i][j+n] = 1;
            cost[i][j+n] = abs(m[i].x - h[j].x) + abs(m[i].y - h[j].y);
            cost[j+n][i] = -cost[i][j+n];       //注意这里加上回流!!!
        }
    }
    for(i = 1; i <= n; i++) cap[0][i] = 1;
    for(i = n+1; i < t; i++) cap[i][t] = 1;
}
 
int solve(int s){
    queue<int> qu;
    int d[maxn];
    memset(flow, 0, sizeof(flow));
    int c = 0;
    for(;;){
        bool inq[maxn];
        memset(d, 0x3f, sizeof(d));
        d[0] = 0;
        memset(inq, 0, sizeof(inq));
        qu.push(s);
        while(!qu.empty()){
            int u = qu.front(); qu.pop();
            inq[u] = 0;
            for(int v = 0; v <= t; v++) if(cap[u][v] > flow[u][v] && d[u] + cost[u][v] < d[v]){
                d[v] = d[u] + cost[u][v];
                p[v] = u;
                if(!inq[v]){
                    qu.push(v);
                    inq[v] = 1;
                }
            }
        }
        if(d[t] == INF) break;
        int a = INF;
        for(int u = t; u != s; u = p[u]) a = min(a, cap[p[u]][u] - flow[p[u]][u]);
        for(int u = t; u != s; u = p[u]){
            flow[p[u]][u] += a;
            flow[u][p[u]] -= a;
        }
        c += d[t] * a;
    }
    return c;
}
 
int main()
{
    int i, j;
    char c;
    while(scanf("%d%d", &N, &M) == 2){
        if(!N && !M) return 0;
        init();
        for(i = 0; i < N; i++){
            getchar();
            for(j = 0; j < M; j++){
                c = getchar();
                if(c == 'H') h[++hid] = (node){i, j};
                if(c == 'm') m[++mid] = (node){i, j};
            }
        }
        build();
        printf("%d\n", solve(0));
    }
    return 0;
}

标签:include,cost,int,flow,最小,Going,MAXN,Home,maxn
来源: https://blog.51cto.com/u_14013325/2895870