HDU 1533 Going Home【最小费用流|KM算法】

N*M图里有x个人x个房间,让每个人进到任意一个房间,一个房间只能容纳一个人,可以经过房间的位置却不进入。每个人都会找个房间进入,求他们走的路程和最短

最小费用流,建图:

源点 对每个人建边 容量为1,费用0,同理房间对汇点容量1,费用0,人和房间容量为1,费用为最小距离(就是x1-x2+y1-y2)。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define eps 10^(-6)
#define Q_CIN ios::sync_with_stdio(false)
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define CLR( a , x ) memset ( a , x , sizeof (a) )
#define RE freopen("1.in","r",stdin);
#define WE freopen("1.out","w",stdout);
#define MOD 10009
#define NMAX 10002
#define min(a,b) ((a)>(b)?(b):(a))
#define max(a,b) ((a)<(b)?(b):(a))
const int inf=0x3f3f3f3f;
const int maxn=103*2;
int ss,tt;
int cap[maxn][maxn],dis[maxn],pre[maxn],inq[maxn],low[maxn],cost[maxn][maxn];
char mp[maxn/2][maxn/2];
struct What{
    int x,y;
}man[maxn/2],house[maxn/2];

void init()
{
    CLR(pre,-1);
    CLR(dis,inf);
    CLR(inq,0);
    CLR(low,inf);
}
int spfa(int s,int t)
{
    queue<int>q;
    q.push(s);
    pre[s]=-1;
    init();
    inq[s]=1;
    dis[s]=0;
    while(!q.empty()){
        int u=q.front();q.pop();inq[u]=0;
        FOR(i,ss,tt){
            if(cap[u][i]&&dis[i]>dis[u]+cost[u][i]){
                dis[i]=dis[u]+cost[u][i];
                pre[i]=u;
                low[i]=min(low[u],cap[u][i]);
                if(!inq[i]){
                    q.push(i);
                    inq[i]=1;
                }
            }
        }
    }
    return dis[t]!=inf;
}
int mincost(int s,int t)
{
    int mincost=0;
    while(spfa(s,t)){
        int u=t;
        while(pre[u]!=-1){
            cap[pre[u]][u]-=low[t];
            cap[u][pre[u]]+=low[t];
            u=pre[u];
        }
        mincost+=dis[t];
    }
    return mincost;
}
int main()
{
//    freopen("1.in","r",stdin);
    int m,n;
    while(cin>>n>>m,m||n){
        CLR(cap,0);
        CLR(cost,inf);
        int manNum=1,houseNum=1;
        FOR(i,1,n)
            FOR(j,1,m){
                cin>>mp[i][j];
                if(mp[i][j]=='m'){
                    man[manNum].x=i;
                    man[manNum].y=j;
                    manNum++;
                }
                else if(mp[i][j]=='H'){
                    house[houseNum].x=i;
                    house[houseNum].y=j;
                    houseNum++;
                }
            }

        int k1=manNum-1;
        FOR(i,1,k1){
            FOR(j,k1+1,k1*2){
                cost[i][j]=abs(man[i].x-house[j-k1].x)+abs(man[i].y-house[j-k1].y);
                cost[j][i]=-cost[i][j];
                cap[i][j]=1;
//                cap[j][i]=0;
            }
        }

        ss=0,tt=k1*2+1;

        FOR(i,1,k1)
            cost[ss][i]=cost[i][ss]=0,cap[ss][i]=1;
        FOR(i,k1+1,k1*2)
            cost[i][tt]=cost[tt][i]=0,cap[i][tt]=1;

        cout<<mincost(ss,tt)<<endl;
    }
    return 0;
}


套KM算法的话就是模板了(快过网络流)

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )

const int N = 310;
const int INF = 0x3f3f3f3f;
int nx, ny; //两边的点数
int g[N][N];//二分图描述
int linker[N], lx[N], ly[N]; //y中各点匹配状态,x,y中的点标号
int slack[N];
bool visx[N], visy[N];
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 mp[105][105];
struct What{
    int x,y;
}man[105],house[105];

int main()
{
//    freopen("1.in","r",stdin);
    int m,n;
    while(cin>>n>>m,m||n){
        int manNum=0,houseNum=0;
        REP(i,n)
            REP(j,m){
                cin>>mp[i][j];
                if(mp[i][j]=='m'){
                    man[manNum].x=i;
                    man[manNum].y=j;
                    manNum++;
                }
                else if(mp[i][j]=='H'){
                    house[houseNum].x=i;
                    house[houseNum].y=j;
                    houseNum++;
                }
            }

        int k1=manNum;
        REP(i,k1)
            REP(j,k1){
                g[i][j]=-(abs(man[i].x-house[j].x)+abs(man[i].y-house[j].y));
            }
        nx=ny=k1;
        cout<<-KM()<<endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值