• POJ


    1、N*M的矩阵中,有k个人和k个房子,每个人分别进入一个房子中,求所有人移动的最小距离。

    2、人看成源点,房子看成汇点,求最小费用最大流。

    建图--

    人指向房子,容量为1,费用为人到房子的曼哈顿距离。

    建立超级源点和超级汇点:超级源点指向人,容量为1,费用为0;房子指向超级汇点,容量为1,费用为0。

    求超级源点到超级汇点的最小费用最大流即可。

    ps:容量为什么都设为1?---有待研究。。

    今天又看了下,设为1是必须的。

    超级源点指向人,容量为1,保证人这个点只有一个人;

    房子指向超级汇点,容量为1,保证一个房子只能容纳一个人。

    然而,人指向房子,容量为1,这并不是必须的,因为前两个条件已经足够了。为了简单,这里仍然把这个容量设为1。

    3、

    1、Bellman-Ford:

    #include<iostream>
    #include<stdio.h>
    #include<vector>
    #include<string.h>
    #include<queue>
    #include<algorithm>
    using namespace std;
    
    const int maxn=256;
    const int INF=0x3f3f3f3f;
    
    struct Edge{
        int from,to,cap,flow,cost;
        Edge(int u,int v,int c,int f,int w):from(u),to(v),cap(c),flow(f),cost(w){}
    };
    
    struct MCMF{
        int n,m;
        vector<Edge>edges;
        vector<int>G[maxn];
        int inq[maxn];//是否在队列中
        int d[maxn];//Bellman-Ford
        int p[maxn];//上一条弧
        int a[maxn];//可改进量
    
        void init(int n){
            this->n=n;
            for(int i=0;i<n;i++)G[i].clear();
            edges.clear();
        }
    
        void AddEdge(int from,int to,int cap,int cost){
            edges.push_back(Edge(from,to,cap,0,cost));
            edges.push_back(Edge(to,from,0,0,-cost));
            m=edges.size();
            G[from].push_back(m-2);
            G[to].push_back(m-1);
        }
    
        bool BellmanFord(int s,int t,int &flow,long long &cost){
            for(int i=0;i<n;i++)d[i]=INF;
            memset(inq,0,sizeof(inq));
            d[s]=0;inq[s]=1;p[s]=0;a[s]=INF;
    
            queue<int>Q;
            Q.push(s);
            while(!Q.empty()){
                int u=Q.front();Q.pop();
                inq[u]=0;
                for(int i=0;i<(int)G[u].size();i++){
                    Edge &e=edges[G[u][i]];
                    if(e.cap>e.flow&&d[e.to]>d[u]+e.cost){
                        d[e.to]=d[u]+e.cost;
                        p[e.to]=G[u][i];
                        a[e.to]=min(a[u],e.cap-e.flow);
                        if(!inq[e.to]){Q.push(e.to);inq[e.to]=1;}
                    }
                }
            }
            if(d[t]==INF)return false;
            flow+=a[t];
            cost+=(long long)d[t]*(long long)a[t];
            for(int u=t;u!=s;u=edges[p[u]].from){
                edges[p[u]].flow+=a[t];
                edges[p[u]^1].flow-=a[t];
            }
            return true;
        }
    
        //需要保证初始网络中没有负权圈
        int MincostMaxflow(int s,int t,long long &cost){
            int flow=0;cost=0;
            while(BellmanFord(s,t,flow,cost));
            return flow;
        }
    }MM;
    
    struct point{//图中的一个点
        int x;
        int y;
    };
    point man[128];//存储人
    point house[128];//存储房子
    int man_sum;//人的总数
    int house_sum;//房子的总数
    
    int main(){
        int N,M;//N*M的矩阵
        char str[128];//
        int S,T;//超级源点,超级汇点
        long long mincost;//最小花费
        int maxflow;//最大流
    
        while(~scanf("%d%d",&N,&M)){
            if(N==0&&M==0)break;
            man_sum=0;
            house_sum=0;
    
            for(int i=0;i<N;++i){
                scanf("%s",str);
                for(int j=0;j<M;++j){
                    if(str[j]=='m'){
                        man[man_sum].x=i;
                        man[man_sum++].y=j;
                    }
                    else if(str[j]=='H'){
                        house[house_sum].x=i;
                        house[house_sum++].y=j;
                    }
                }
            }
    
            MM.init(man_sum+house_sum+2);
            for(int i=0;i<man_sum;++i){
                for(int j=0;j<house_sum;++j){
                    MM.AddEdge(i,man_sum+j,1,abs(man[i].x-house[j].x)+abs(man[i].y-house[j].y));
                }
            }
            //超级源点
            S=man_sum+house_sum;
            for(int i=0;i<man_sum;++i){
                MM.AddEdge(S,i,1,0);
            }
            //超级汇点
            T=man_sum+house_sum+1;
            for(int i=0;i<house_sum;++i){
                MM.AddEdge(man_sum+i,T,1,0);
            }
    
            maxflow=MM.MincostMaxflow(S,T,mincost);
            printf("%lld
    ",mincost);
        }
        return 0;
    }
    View Code

    2、SPFA版费用流:

    /*
    SPFA版费用流
    最小费用最大流,求最大费用最大流只需要取相反数,结果取相反数即可。
    点的总数为N,点的编号0~N-1
    */
    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    #include<queue>
    #include<algorithm>
    using namespace std;
    
    const int MAXN=256;
    const int MAXM=20500;
    const int INF=0x3f3f3f3f;
    struct Edge{
        int to,next,cap,flow,cost;
    }edge[MAXM];
    int head[MAXN],tol;
    int pre[MAXN],dis[MAXN];
    bool vis[MAXN];
    int N;//节点总个数,节点编号从0~N-1
    void init(int n){
        N=n;
        tol=0;
        memset(head,-1,sizeof(head));
    }
    void addedge(int u,int v,int cap,int cost){
        edge[tol].to=v;
        edge[tol].cap=cap;
        edge[tol].cost=cost;
        edge[tol].flow=0;
        edge[tol].next=head[u];
        head[u]=tol++;
        edge[tol].to=u;
        edge[tol].cap=0;
        edge[tol].cost=-cost;
        edge[tol].flow=0;
        edge[tol].next=head[v];
        head[v]=tol++;
    }
    bool spfa(int s,int t){
        queue<int>q;
        for(int i=0;i<N;i++){
            dis[i]=INF;
            vis[i]=false;
            pre[i]=-1;
        }
        dis[s]=0;
        vis[s]=true;
        q.push(s);
        while(!q.empty()){
            int u=q.front();
            q.pop();
            vis[u]=false;
            for(int i=head[u];i!=-1;i=edge[i].next){
                int v=edge[i].to;
                if(edge[i].cap>edge[i].flow&&dis[v]>dis[u]+edge[i].cost){
                    dis[v]=dis[u]+edge[i].cost;
                    pre[v]=i;
                    if(!vis[v]){
                        vis[v]=true;
                        q.push(v);
                    }
                }
            }
        }
        if(pre[t]==-1)return false;
        else return true;
    }
    //返回的是最大流,cost存的是最小费用
    int minCostMaxflow(int s,int t,int &cost){
        int flow=0;
        cost=0;
        while(spfa(s,t)){
            int Min=INF;
            for(int i=pre[t];i!=-1;i=pre[edge[i^1].to]){
                if(Min>edge[i].cap-edge[i].flow)
                    Min=edge[i].cap-edge[i].flow;
            }
            for(int i=pre[t];i!=-1;i=pre[edge[i^1].to]){
                edge[i].flow+=Min;
                edge[i^1].flow-=Min;
                cost+=edge[i].cost*Min;
            }
            flow+=Min;
        }
        return flow;
    }
    
    struct point{//图中的一个点
        int x;
        int y;
    };
    point man[128];//存储人
    point house[128];//存储房子
    int man_sum;//人的总数
    int house_sum;//房子的总数
    
    int main(){
        int N,M;//N*M的矩阵
        char str[128];
        int S,T;//超级源点,超级汇点
        int mincost;//最小花费
        int maxflow;//最大流
    
        while(~scanf("%d%d",&N,&M)){
            if(N==0&&M==0)break;
            man_sum=0;
            house_sum=0;
    
            for(int i=0;i<N;++i){
                scanf("%s",str);
                for(int j=0;j<M;++j){
                    if(str[j]=='m'){
                        man[man_sum].x=i;
                        man[man_sum++].y=j;
                    }
                    else if(str[j]=='H'){
                        house[house_sum].x=i;
                        house[house_sum++].y=j;
                    }
                }
            }
    
            init(man_sum+house_sum+2);
            for(int i=0;i<man_sum;++i){
                for(int j=0;j<house_sum;++j){
                    addedge(i,man_sum+j,1,abs(man[i].x-house[j].x)+abs(man[i].y-house[j].y));
                }
            }
            //超级源点
            S=man_sum+house_sum;
            for(int i=0;i<man_sum;++i){
                addedge(S,i,1,0);
            }
            //超级汇点
            T=man_sum+house_sum+1;
            for(int i=0;i<house_sum;++i){
                addedge(man_sum+i,T,1,0);
            }
    
            maxflow=minCostMaxflow(S,T,mincost);
            printf("%d
    ",mincost);
        }
        return 0;
    }
    View Code

    3、zkw费用流:

    /*
    zkw费用流
    对于二分图类型的比较高效
    */
    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    
    const int MAXN=256;
    const int MAXM=20500;
    const int INF=0x3f3f3f3f;
    struct Edge{
        int to,next,cap,flow,cost;
        Edge(int _to=0,int _next=0,int _cap=0,int _flow=0,int _cost=0):
            to(_to),next(_next),cap(_cap),flow(_flow),cost(_cost){}
    }edge[MAXM];
    struct ZKW_MinCostMaxFlow{
        int head[MAXN],tot;
        int cur[MAXN];
        int dis[MAXN];
        bool vis[MAXN];
        int ss,tt,N;//源点、汇点和点的总个数(编号是0~N-1),不需要额外赋值,调用会直接赋值
        int min_cost,max_flow;
        void init(){
            tot=0;
            memset(head,-1,sizeof(head));
        }
        void addedge(int u,int v,int cap,int cost){
            edge[tot]=Edge(v,head[u],cap,0,cost);
            head[u]=tot++;
            edge[tot]=Edge(u,head[v],0,0,-cost);
            head[v]=tot++;
        }
        int aug(int u,int flow){
            if(u==tt)return flow;
            vis[u]=true;
            for(int i=cur[u];i!=-1;i=edge[i].next){
                int v=edge[i].to;
                if(edge[i].cap>edge[i].flow&&!vis[v]&&dis[u]==dis[v]+edge[i].cost){
                    int tmp=aug(v,min(flow,edge[i].cap-edge[i].flow));
                    edge[i].flow+=tmp;
                    edge[i^1].flow-=tmp;
                    cur[u]=i;
                    if(tmp)return tmp;
                }
            }
            return 0;
        }
        bool modify_label(){
            int d=INF;
            for(int u=0;u<N;u++)
                if(vis[u])
                    for(int i=head[u];i!=-1;i=edge[i].next){
                        int v=edge[i].to;
                        if(edge[i].cap>edge[i].flow&&!vis[v])
                            d=min(d,dis[v]+edge[i].cost-dis[u]);
                    }
            if(d==INF)return false;
            for(int i=0;i<N;i++)
                if(vis[i]){
                    vis[i]=false;
                    dis[i]+=d;
                }
            return true;
        }
        /*
        直接调用获取最小费用和最大流
        输入:start-源点,end-汇点,n-点的总个数(编号从0开始)
        返回值:pair<int,int>第一个是最小费用,第二个是最大流
        */
        pair<int,int> mincostmaxflow(int start,int end,int n){
            ss=start,tt=end,N=n;
            min_cost=max_flow=0;
            for(int i=0;i<n;i++)dis[i]=0;
            while(1){
                for(int i=0;i<n;i++)cur[i]=head[i];
                while(1){
                    for(int i=0;i<n;i++)vis[i]=false;
                    int tmp=aug(ss,INF);
                    if(tmp==0)break;
                    max_flow+=tmp;
                    min_cost+=tmp*dis[ss];
                }
                if(!modify_label())break;
            }
            return make_pair(min_cost,max_flow);
        }
    }solve;
    
    
    struct point{//图中的一个点
        int x;
        int y;
    };
    point man[128];//存储人
    point house[128];//存储房子
    int man_sum;//人的总数
    int house_sum;//房子的总数
    
    int main(){
        int N,M;//N*M的矩阵
        char str[128];
        int S,T;//超级源点,超级汇点
        //int mincost;
        //int maxflow;
        pair<int,int>pr;//最小花费,最大流
    
        while(~scanf("%d%d",&N,&M)){
            if(N==0&&M==0)break;
            man_sum=0;
            house_sum=0;
    
            for(int i=0;i<N;++i){
                scanf("%s",str);
                for(int j=0;j<M;++j){
                    if(str[j]=='m'){
                        man[man_sum].x=i;
                        man[man_sum++].y=j;
                    }
                    else if(str[j]=='H'){
                        house[house_sum].x=i;
                        house[house_sum++].y=j;
                    }
                }
            }
    
            solve.init();
            for(int i=0;i<man_sum;++i){
                for(int j=0;j<house_sum;++j){
                    solve.addedge(i,man_sum+j,1,abs(man[i].x-house[j].x)+abs(man[i].y-house[j].y));
                }
            }
            //超级源点
            S=man_sum+house_sum;
            for(int i=0;i<man_sum;++i){
                solve.addedge(S,i,1,0);
            }
            //超级汇点
            T=man_sum+house_sum+1;
            for(int i=0;i<house_sum;++i){
                solve.addedge(man_sum+i,T,1,0);
            }
    
            pr=solve.mincostmaxflow(S,T,man_sum+house_sum+2);
            printf("%d
    ",pr.first);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    jdbctemplate的batchUpdate使用方法
    js利用html5的canvas实现图像等比例压缩
    js前台通过EXIF.js获取图片中携带的经纬度信息
    Linux expr相关
    expect用于scp传输文件
    linux 自动登录ftp 获取文件
    作业8:单元测试练习(个人练习)
    作业7-用户体验设计案例分析
    作业6:团队作业——学生成绩录入系统设计与实现
    作业5:需求分析
  • 原文地址:https://www.cnblogs.com/gongpixin/p/4995928.html
Copyright © 2020-2023  润新知