• Going Home


    题目大意:在一个网格里面有n个小男人和n个房子,现在想让每个小男人都有一个房子住,不过每个人移动一下都需要花费¥1,现在求出来最小的总花费。ps:可以认为网格的每个点都是很大的广场并且容纳所有的人,人可以走在有房子的点但是不进入房子。

    分析:人-房子,很完美的带全都最小值匹配啊,人到一个房子的花费就是他们之间的曼哈顿距离,用这些距离构造一个二分图,然后用KM算法求出来最小费。下面是KM算法
    ***********************************************************************************************************************************
    #include<stdio.h>
    #include<string.h>
    #include<queue>
    #include<algorithm>
    #include<math.h>
    using namespace std;

    const int MAXN = 107;
    const int oo = 1e9+7;

    struct point{int x, y;}man[MAXN], house[MAXN];
    char G[MAXN][MAXN];
    int w[MAXN][MAXN], slack[MAXN], Nx, Ny;
    int dx[MAXN], dy[MAXN], Ly[MAXN];
    bool vx[MAXN], vy[MAXN];

    void InIt()
    {
        Nx = Ny = 0;

        for(int i=1; i<MAXN; i++)
        {
            dx[i] = -oo;
            dy[i] = 0;
        }

    }
    bool Find(int i)
    {
        vx[i] = true;
        for(int j=1; j<=Ny; j++)
        {
            if(!vy[j] && w[i][j] == dx[i]+dy[j])
            {
                vy[j] = true;
                if(!Ly[j] || Find(Ly[j]))
                {
                    Ly[j] = i;
                    return true;
                }
            }
            else if(!vy[j])
                slack[j] = min(slack[j], dx[i]+dy[j]-w[i][j]);
        }

        return false;
    }
    int KM()
    {
        int i, j;

        memset(Ly, 0sizeof(Ly));

        for(i=1; i<=Nx; i++)
        {
            for(j=1; j<=Ny; j++)
                slack[j] = oo;
            while(true)
            {
                memset(vx, falsesizeof(vx));
                memset(vy, falsesizeof(vy));

                if(Find(i) == true)
                    break;

                int d = oo;

                for(j=1; j<=Ny; j++)
                {
                    if(!vy[j] && d > slack[j])
                        d = slack[j];
                }

                for(j=1; j<=Nx; j++)
                {
                    if(vx[j])
                        dx[j] -= d;
                }
                for(j=1; j<=Ny; j++)
                {
                    if(vy[j])
                        dy[j] += d;
                    else
                        slack[j] -= d;
                }
            }
        }

        int sum = 0;

        for(i=1; i<=Ny; i++)
            sum += w[Ly[i]][i];

        return -sum;
    }

    int main()
    {
        int M, N;

        while(scanf("%d%d", &M, &N), M+N)
        {
            int i, j;

            InIt();

            for(i=0; i<M; i++)
                scanf("%s", G[i]);

            for(i=0; i<M; i++)
            for(j=0; j<N; j++)
            {
                if(G[i][j] == 'm')
                {
                    Nx++;
                    man[Nx].x = i;
                    man[Nx].y = j;
                }
                if(G[i][j] == 'H')
                {
                    Ny++;
                    house[Ny].x = i;
                    house[Ny].y = j;
                }
            }

            for(i=1; i<=Nx; i++)
            for(j=1; j<=Ny; j++)
            {
                w[i][j] = fabs(man[i].x-house[j].x) + fabs(man[i].y-house[j].y);
                w[i][j] = -w[i][j];
                dx[i] = max(dx[i], w[i][j]);
            }

            printf("%d ", KM());
        }

        return 0;
    }
    View Code

    研究了一下网络网络流里面的最小费用最大流,听着名字比较高大上吧,看了半天终于恍然大悟,其实还是最大流。但是为什么又叫最小费用呢?所谓的最小费用并不是从源点到汇点的最小费用,而是再保证最大流的前提下的最小费用,我们求最大流的的办法就是不断进行路径增广,而怎么增广路径就比较随意了,可以用深搜或者广搜,不管怎么样只要找到可以进行增广的路就行,如果在这个前提下我们找增广路的时候用最短路的办法去找,那么岂不是找的每条路都是花费最小的,而且最后无路时候就是最小花费了(同时也求出来了最大流),有了这个认识就可以写最消费最大流了。

    下面是用spfa(可以处理负权边)实现的。
    ***********************************************************************************************************************************
    #include<stdio.h>
    #include<string.h>
    #include<queue>
    #include<stack>
    #include<algorithm>
    #include<math.h>
    using namespace std;

    const int MAXN = 407;
    const int oo = 1e9+7;

    struct point{int x, y;}man[MAXN], house[MAXN];
    struct Graph{int flow, cost;}G[MAXN][MAXN];
    int NX, NY, start, End;///男人和房子的数目,源点和汇点

    bool spfa(int pre[])
    {
        stack<int> sta;
        int instack[MAXN]={0}, dist[MAXN];

        for(int i=1; i<=End; i++)
            dist[i] = oo;

        dist[start] = 0;
        sta.push(start);

        while(sta.size())
        {
            int u = sta.top();sta.pop();
            instack[u] = false;

            for(int i=1; i<=End; i++)
            {
                if(G[u][i].flow &&  dist[i] > dist[u]+G[u][i].cost)
                {
                    dist[i] = dist[u] + G[u][i].cost;
                    pre[i] = u;

                    if(instack[i] == false)
                    {
                        sta.push(i);
                        instack[i] = true;
                    }
                }
            }
        }

        return dist[End] != oo;
    }
    int MinCost()
    {
        int i, pre[MAXN], cost=0;

        while(spfa(pre) == true)
        {///如果有增广路
            int MinFlow = oo;

            for(i=End; i != start; i=pre[i])
                MinFlow = min(MinFlow, G[pre[i]][i].flow);
            for(i=End; i != start; i=pre[i])
            {///逆向访问这条增广路上的每条边
                int k = pre[i];
                G[k][i].flow -= MinFlow;
                G[i][k].flow += MinFlow;
                cost += G[k][i].cost;
            }
        }

        return cost;
    }

    int main()
    {
        int M, N;

        while(scanf("%d%d", &M, &N), M+N)
        {
            int i, j;char s[MAXN][MAXN];

            memset(G, 0sizeof(G));
            NX = NY = 0;

            for(i=0; i<M; i++)
                scanf("%s", s[i]);

            for(i=0; i<M; i++)
            for(j=0; j<N; j++)
            {
                if(s[i][j] == 'm')
                {
                    NX++;
                    man[NX].x = i;
                    man[NX].y = j;
                }
                if(s[i][j] == 'H')
                {
                    NY++;
                    house[NY].x = i;
                    house[NY].y = j;
                }
            }

            for(i=1; i<=NX; i++)
            for(j=1; j<=NY; j++)
            {///房子的编号从NX~NX+NY
                G[i][NX+j].flow = 1;
                G[i][NX+j].cost = fabs(man[i].x-house[j].x)+fabs(man[i].y-house[j].y);
                G[NX+j][i].cost = -G[i][NX+j].cost;
            }

            start = NX+NY+1, End = start+1;

            for(i=1; i<=NX; i++)
            {///把源点与人连接
                G[start][i].flow = 1;
                G[start][i].cost = 0;
            }
            for(i=1; i<=NY; i++)
            {///把房子和汇点连接
                G[NX+i][End].flow = 1;
                G[NX+i][End].cost = 0;
            }

            printf("%d ", MinCost());
        }

        return 0;
    }
    View Code
  • 相关阅读:
    SQL语句大全
    软件设计方法
    统计在线的用户
    解放web程序员的输入验证
    OUTLOOK菜单类
    在asp.net 2.0中结合母板页meta,Tiele重置
    微软自带AJAX的用法
    在asp.net 2.0中发送邮件
    js编写的语法高亮引擎
    有关模版MasterPage的问题
  • 原文地址:https://www.cnblogs.com/liuxin13/p/4713278.html
Copyright © 2020-2023  润新知