poj 2195 费用流模板题

费用流模板题
每个人到每个房子建立一条流量为1,费用为曼哈顿距离的边,然后跑一遍最小费用流即可;

#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <math.h>
#include <queue>

using namespace std;
const int maxn=1e4+7,maxm=2e5+7;
int h[maxn],e[maxm],ne[maxm],w[maxm],f[maxm],idx;
int dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool vis[maxn];
int n,m,k1,k2,s,t,max_flow,min_cost;
struct home{
    int x,y;
}ho[maxn];
struct man{
    int x,y;
}ma[maxn];

void add(int a,int b,int c,int d){
    e[idx]=b,w[idx]=c,f[idx]=d,ne[idx]=h[a],h[a]=idx++;
}
inline int manhattan(man a,home b){
    return abs(a.x-b.x)+abs(a.y-b.y);
}
bool spfa(int s,int t){
    memset(dis,0x3f,sizeof dis);memset(flow,0x3f,sizeof flow);pre[t]=-1;
    queue<int>q;q.push(s),vis[s]=1,dis[s]=0;
    while(q.size()){
        int x=q.front();q.pop();vis[x]=0;
        for(int i=h[x];~i;i=ne[i]){
            int j=e[i];
            if(dis[j]>dis[x]+f[i]&&w[i]){
                dis[j]=dis[x]+f[i];
                flow[j]=min(flow[x],w[i]);
                last[j]=i,pre[j]=x;
                if(!vis[j])q.push(j),vis[j]=1;
            }
        }
    }
    return ~pre[t];
}
void solve(){
    while(spfa(s,t)){
        min_cost+=flow[t]*dis[t];
        max_flow+=flow[t];
        int p=t;
        while(p!=s){
            w[last[p]]-=flow[t];
            w[last[p]^1]+=flow[t];
            p=pre[p];
        }
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m)&&(n||m)){
        memset(h,-1,sizeof h),idx=0;char c;k1=k2=0;min_cost=max_flow=0;
        for(int i=1;i<=n;i++){
            char c[maxn];scanf("%s",c+1);
            for(int j=1;j<=m;j++){
                if(c[j]=='H')ho[++k1]={i,j};
                else if(c[j]=='m')ma[++k2]={i,j};
            }
        }
        s=0,t=k1+k2+2;
        for(int i=1;i<=k2;i++)
            for(int j=1;j<=k1;j++){
                int x=manhattan(ma[i],ho[j]);
                add(i,k2+j,1,x),add(k2+j,i,0,-x);
            }
        for(int i=1;i<=k2;i++)add(s,i,1,0),add(i,s,0,0);
        for(int i=1;i<=k1;i++)add(k2+i,t,1,0),add(t,k2+i,1,0);
        solve();
        printf("%d\n",min_cost);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值