POJ 2195 Going Home 最小费用最大流 or KM算法

题目大意是一张地图中,有n个人要走回n个房子里,然后人只能横着或竖着走一格,求他们回家的距离总和最短。

这道题看起来是个最优匹配问题,用KM或者最小费用最大流做

首先把每个人与每个房子之间的距离求出来

然后就是套用模板了

#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <cmath>
#include <ctime>

using namespace std;
struct point
{
    int x, y;
}p[105], h[105];
int m, n;
int d[105][105];
char s[105][105];
int tot = 0;
class mincost
{
private:
    const static int V = 205; //注意点的个数, 应该为人+房间+2, 所以至少要开到202
    const static int E = 25015;
    const static int INF = -1u >> 1;
    struct Edge
    {
        int v, cap, cost;
        Edge *next;
    } pool[E], *g[V], *pp, *pree[V];
    int T, S, dis[V], pre[V];
    int n, m, flow, cirq[V];
    void SPFA();
    inline void addedge(int i, int j, int cap, int cost);
public:
    bool initialize(int x, int y);
    void mincost_maxflow();
};

void mincost::mincost_maxflow()
{
    while (true)
    {
        SPFA();
        if (dis[T] == INF)
            break;
        int minn = INF;
        for (int i = T; i != S; i = pre[i])
            minn = min(minn, pree[i]->cap);
        for (int i = T; i != S; i = pre[i])
        {
            pree[i]->cap -= minn;
            pool[(pree[i] - pool)^0x1].cap += minn;
            flow += minn * pree[i]->cost;

        }
        tot += minn;  //流量计算
    }
    printf("%d\n", flow);
}

void mincost::SPFA()
{
    bool vst[V] = {false};
    int tail = 0, u;
    fill(dis,dis + n,0x7fffffff);
    cirq[0] = S;
    vst[S] = 1;
    dis[S] = 0;
    for (int i = 0; i <= tail; i++)
    {
        int v = cirq[i % n];
        for (Edge *i = g[v]; i != NULL; i = i->next)
        {
            if (!i->cap)
                continue;
            u = i->v;
            if (i->cost + dis[v] < dis[u])
            {
                dis[u] = i->cost + dis[v];
                pree[u] = i;
                pre[u] = v;
                if (!vst[u])
                {
                    tail++;
                    cirq[tail % n] = u;
                    vst[u] = true;
                }
            }
        }
        vst[v] = false;
    }
}

void mincost::addedge(int i, int j, int cap, int cost)
{
    pp->cap = cap;
    pp->v = j;
    pp->cost = cost;
    pp->next = g[i];
    g[i] = pp++;
}
bool mincost::initialize(int x, int y)
{
    memset(g, 0, sizeof (g));
    pp = pool;
    n = x + y + 2;  //n即为顶点的个数
    S = 0;
    T = x + y + 1;
    for(int i = 1; i <= x; i++)
    {
        for(int j = 1; j <= y; j++)
        {
            addedge(i, x + j, 1, d[i][j]);
            addedge(x + j, i, 0, -d[i][j]);
        }
    }
    for(int i = 1; i <= x; i++)
    {
        addedge(0, i, 1, 0);
        addedge(i, 0, 0, 0);
    }
    for(int i = 1; i <= y; i++)
    {
        addedge(x + i, T, 1, 0);
        addedge(T, x + i, 0, 0);
    }
    flow = 0;
    return true;
}
mincost g;
int main()
{
    while(scanf("%d%d", &m, &n) != EOF)
    {
        if(m == 0 && n == 0) break;
        for(int i = 0; i < m; i++)
            scanf("%s", s[i]);
        int hcnt = 0, pcnt = 0;
        for(int i = 0; i < m; i++)
        {
            for(int j = 0; j < n; j++)
            {
                if(s[i][j] == 'H')
                {
                    hcnt++;
                    h[hcnt].x = i;
                    h[hcnt].y = j;
                }
                else if(s[i][j] == 'm')
                {
                    pcnt++;
                    p[pcnt].x = i;
                    p[pcnt].y = j;
                }
            }
        }
        for(int i = 1; i <= pcnt; i++)
        {
            for(int j = 1; j <= hcnt; j++)
            {
                d[i][j] = abs(p[i].x - h[j].x) + abs(p[i].y - h[j].y);
            }
        }
        g.initialize(pcnt, hcnt);
        tot = 0;
        g.mincost_maxflow();
    }
    return 0;
}


KM算法版本, 用的DD神牛的模板http://cuitianyi.com/blog/%E6%B1%82%E6%9C%80%E5%A4%A7%E6%9D%83%E4%BA%8C%E5%88%86%E5%8C%B9%E9%85%8D%E7%9A%84km%E7%AE%97%E6%B3%95/

另外 芳哥blog里有篇介绍http://blog.csdn.net/wsniyufang/article/details/6759628

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#define MAXN 105
#define MAXM 555555
#define INF 1000000000
using namespace std;
char mp[MAXN][MAXN];
int n, m, ny, nx;
int w[MAXN][MAXN];
int lx[MAXN], ly[MAXN];
int linky[MAXN];
int visx[MAXN], visy[MAXN];
int slack[MAXN];
struct P
{
    int x, y;
}house[MAXN], man[MAXN];
bool find(int x)
{
    visx[x] = 1;
    for(int y = 1; y <= ny; y++)
    {
        if(visy[y]) continue;
        int t = lx[x] + ly[y] - w[x][y];
        if(t == 0)
        {
            visy[y] = 1;
            if(linky[y] == -1 || find(linky[y]))
            {
                linky[y] = x;
                return true;
            }
        }
         else if(slack[y] > t) slack[y] = t;
    }
    return false;
}
void KM()
{
    memset(linky, -1, sizeof(linky));
    for(int i = 1; i <= nx; i++) lx[i] = -INF;
    memset(ly, 0, sizeof(ly));
    for(int i = 1; i <= nx; i++)
        for(int j = 1; j <= ny; j++)
            if(w[i][j] > lx[i]) lx[i] = w[i][j];
    for(int x = 1; x <= nx; x++)
    {
        for(int i = 1; i <= ny; i++) slack[i] = INF;
        while(true)
        {
            memset(visx, 0, sizeof(visx));
			memset(visy, 0, sizeof(visy));
			if(find(x)) break;
			int d = INF;
			for(int i = 1; i <= ny; i++)
                if(!visy[i]) d = min(d, slack[i]);
            for(int i = 1; i <= nx; i++)
                if(visx[i]) lx[i] -=d;
            for(int i = 1; i <= ny; i++)
                if(visy[i]) ly[i] += d;
                else slack[i] -= d;
        }
    }
}
int main()
{
    while(scanf("%d%d", &n, &m) != EOF)
    {
        if(n == 0 && m == 0) break;
        ny = 0, nx = 0;
        for(int i = 0; i < n; i++) scanf("%s", mp[i]);
        for(int i = 0; i < n; i++)
            for(int j = 0; j < m; j++)
                if(mp[i][j] == 'H') {++ny; house[ny].x = i, house[ny].y = j;}
                else if(mp[i][j] == 'm') {++nx; man[nx].x = i, man[nx].y = j;}
        for(int i = 1; i <= nx; i++)
            for(int j = 1; j <= ny; j++)
                w[i][j] = -(abs(house[j].x - man[i].x) + abs(house[j].y - man[i].y));
        KM();
        int ans = 0;
        for(int i = 1; i <= ny; i++) ans += w[linky[i]][i];
        printf("%d\n", -ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值