poj2195 -最小费用流

来源:互联网 发布:淘宝女靴新款 编辑:程序博客网 时间:2024/04/29 10:20
Going Home
Time Limit: 1000MS Memory Limit: 65536KTotal Submissions: 21616 Accepted: 10919

Description

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.mH.5 5HH..m...............mm..H7 8...H.......H.......H....mmmHmmmm...H.......H.......H....0 0

Sample Output

210

28

题意:一个n*m的棋盘,H表示房子,m表示人,每个人都要到一个房子里去,走一个格子要花费1美元,问所有人回到房子里总花费

最少是多少;

每一个人去一个房子,不能两个人同时去一个房子,显然可以看成二分图最小权匹配,一个KM算法可以搞定,房子看成X集合人看成

Y集合,集合间每个人与每个房子两两建边,权值为负的两者距离,然后就没有然后了,KM算法搞定

#include <stdio.h>#include <string.h>#include <math.h>#include <stdlib.h>#include <algorithm>#include <iostream>#include <queue>using namespace std;const int maxn=105,inf=0x3f3f3f3f;int g[maxn][maxn];int nx,ny;int linker[maxn],lx[maxn],ly[maxn];int slack[maxn];bool visx[maxn],visy[maxn];struct node{    int i,j;} H[maxn],M[maxn];int solve(int x,int y){    return abs(H[x].i-M[y].i)+abs(H[x].j-M[y].j);}bool dfs(int x){    visx[x]=true;    for(int y=0; y<ny; y++)    {        if(visy[y])            continue;        int tmp=lx[x]+ly[y]-g[x][y];        if(tmp==0)        {            visy[y]=true;            if(linker[y]==-1||dfs(linker[y]))            {                linker[y]=x;                return true;            }        }        else if(slack[y]>tmp)            slack[y]=tmp;    }    return false;}int KM(){    memset(linker,-1,sizeof(linker));    memset(ly,0,sizeof(ly));    for(int i=0; i<nx; i++)    {        lx[i]=-inf;        for(int j=0; j<ny; j++)        {            if(g[i][j]>lx[i])                lx[i]=g[i][j];        }    }    for(int x=0; x<nx; x++)    {        for(int i=0; i<ny; i++)            slack[i]=inf;        while(true)        {            memset(visx,false,sizeof(visx));            memset(visy,false,sizeof(visy));            if(dfs(x))break;            int d=inf;            for(int i=0; i<ny; i++)            {                if(!visy[i]&&d>slack[i])                  d=slack[i];            }            for(int i=0; i<nx; i++)            {                if(visx[i])                    lx[i]-=d;            }            for(int i=0; i<ny; i++)            {                if(visy[i])                    ly[i]+=d;                else                    slack[i]-=d;            }        }    }    int res=0;    for(int i=0; i<ny; i++)        if(linker[i]!=-1)            res+=g[linker[i]][i];    return res;}char str[maxn][maxn];int main(){    int n,m;    int tom,toh;    while(scanf("%d%d",&n,&m)!=-1)    {        if(n==0&m==0)            break;        toh=tom=0;        for(int i=0; i<n; i++)        {            scanf("%s",str[i]);            for(int j=0; j<m; j++)            {                if(str[i][j]=='H')                {                    H[toh].i=i;                    H[toh++].j=j;                }                if(str[i][j]=='m')                {                    M[tom].i=i;                    M[tom++].j=j;                }            }        }        ny=tom;        nx=toh;        for(int i=0; i<toh; i++)        {            for(int j=0; j<tom; j++)            {                g[i][j]=-solve(i,j);            }        }        printf("%d\n",-KM());    }    return 0;}
第二种算法是最小费用最大流,同理X Y 集合房子与人两两建边,流量为1,花费为距离,然后建立源点汇点,源点

连接X集合,Y集合连接汇点,流量为1,费用为0,因为从源点到X集合每条边使用过流量就减为0了,Y集到汇点每条边使用

过后流量减为0,也就是说X Y 集合 每个点只能用一次,用过后流量就没有了,每次优先选择花费最小的路径,总话费肯定也是最小

的,最小话费就是我们要的结果了;

--模板大法好--

#include <stdio.h>#include <string.h>#include <math.h>#include <stdlib.h>#include <algorithm>#include <iostream>#include <queue>using namespace std;const int maxn=1e5+5,inf=0x3f3f3f3f;struct Edge{    int u,to,next,cap,cost;}edge[maxn];int tot,head[maxn];int vis[maxn],dis[maxn],pre[maxn];char str[105][105];struct node{    int i,j;}H[maxn],M[maxn];void init(){    tot=0;    memset(head,-1,sizeof(head));}void addedge(int u,int v,int cap,int cost){    edge[tot].u=u;    edge[tot].to=v;    edge[tot].next=head[u];    edge[tot].cap=cap;    edge[tot].cost=cost;    head[u]=tot++;    edge[tot].u=v;    edge[tot].to=u;    edge[tot].cap=0;    edge[tot].cost=-cost;///花费必须是负值    edge[tot].next=head[v];    head[v]=tot++;}bool spfa(int start,int end){    int u,v;    queue<int>que;    for(int i=0;i<end+2;i++)    {        pre[i]=-1;        vis[i]=0;        dis[i]=inf;    }    vis[start]=1;    dis[start]=0;    que.push(start);    while(!que.empty())    {        u=que.front();        que.pop();        vis[u]=0;        for(int i=head[u];i!=-1;i=edge[i].next)        {            cout<<edge[i].cost<<endl;            if(edge[i].cap)///有流量才有最短路啊            {                v=edge[i].to;                if(dis[v]>dis[u]+edge[i].cost)                {                    dis[v]=dis[u]+edge[i].cost;                    pre[v]=i;                    if(!vis[v])                    {                        vis[v]=1;                        que.push(v);                    }                }            }        }    }    return dis[end]!=inf;}int mcMF(int start,int end){    int ans=0;    //int flow_sum=0;    while(spfa(start,end))    {       /* flow=inf;        for(int i=pre[end];i!=-1;i=pre[edge[i].u])        {            if(edge[i].cap<flow)                flow=edge[i].cap;        }*/        for(int i=pre[end];i!=-1;i=pre[edge[i].u])        {            edge[i].cap-=1;///流量都是1 减1就好了,所以上面求最小流量的过程就省去了            edge[i^1].cap+=1;        }        ans+=dis[end];    }    return ans;}int solve(int x,int y){    return abs(H[x].i-M[y].i)+abs(H[x].j-M[y].j);}int main(){    int n,m;    int tom,toh;    int S,T;    while(scanf("%d%d",&n,&m)!=-1)    {        if(n==0&m==0)            break;        toh=tom=0;        S=20004;        T=20005;        int k=10001;  ///100*100 的矩阵,所以设置k=10001        init();        for(int i=0;i<n;i++)        {            scanf("%s",str[i]);            for(int j=0;j<m;j++)            {                if(str[i][j]=='H')                {                    H[toh].i=i;                    H[toh].j=j;                    addedge(S,toh,1,0);                    toh++;                }                if(str[i][j]=='m')                {                    M[tom].i=i;                    M[tom].j=j;                    addedge(tom+k,T,1,0);                    tom++;                }            }        }        for(int i=0;i<toh;i++)        {            for(int j=0;j<tom;j++)            {                addedge(i,j+k,1,solve(i,j));            }        }        printf("%d\n",mcMF(S,T));    }    return 0;}



0 0
原创粉丝点击