POJ 2195 Going Home (最小费用最大流)

题目链接
第一次写最小费用最大流
最小费用最大流相当于最短路和最大流的结合,建立了残量图后,费用作为距离
然后bfs遍历源点到汇点的最小费用,用数组记录遍历路径,而后通过最大流的做法
对残量图进行更新,寻找路径中的最小流量得到的就是最小费用最大流。
题目大意:图中有若干个m和H,每个m都要回到一个H,问最少走多少步
这里可以把走的步数定位费用
于是可以建图
源点 链接 人 流量为1 费用为0
人 链接 家 流量为 1 费用为bfs得到的最小步数
家 链接 汇点 流量为 1 费用为 0
跑一遍最小费用最大流就可以得到答案啦

#include<stdio.h>
#include<queue>
#include<string.h>
#include<vector>
using namespace std;
#define MAXN 1005
#define INF 2000000000
int source,sink; //源点 汇点 
struct tree
{
    int from,to,flow,worth,next; //结点,流量,费用,链表 
    tree(){}
    tree(int fr,int ro,int fl,int wo,int ne)
    {
        from=fr,to=ro,flow=fl,worth=wo,next=ne;
    }
}e[MAXN*MAXN];
int g[MAXN]; // 建立链表
int num;  //边数
void init() //初始化
{
    memset(g,0,sizeof(g));
    num=1;
}
void addtree(int from,int to,int flow,int worth)  //建图 
{
    e[++num]=tree(from,to,flow,worth,g[from]);
    g[from]=num;
    e[++num]=tree(to,from,0,-worth,g[to]);    //反向弧 
    g[to]=num;
}
bool visque[MAXN]; //查看是否入队
int dis[MAXN]; //最小距离
int pre[MAXN],prx[MAXN]; //记录路线用于更新残量图 
queue<int>q;
int bfs() //寻找最短路
{

    while(!q.empty()) q.pop(); //初始化队列
    for(int i=0;i<=MAXN;i++) dis[i]=INF; //初始化距离 
    q.push(source); //源点入队
    dis[source]=0;
    visque[source]=true;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=g[u];i;i=e[i].next)
        {
            if(e[i].flow>0&&dis[u]+e[i].worth<dis[e[i].to]) //更新最短路 
            {
                dis[e[i].to]=dis[u]+e[i].worth;
                pre[e[i].to]=u;
                prx[e[i].to]=i;
                if(!visque[e[i].to])
                {
                    visque[e[i].to]=true;
                    q.push(e[i].to);
                }
            }
        }
        visque[u]=false; //前面已经让u出队了所以这里要写一下 
    } 
    return dis[sink]!=INF; //判断是否可以到达汇点 
} 
int dfs()
{
    int u=sink;
    int ans=INF;
    while(u!=source) //找当前路中的最小流量 
    {
        if(e[prx[u]].flow<ans) ans=e[prx[u]].flow;
        u=pre[u];
    }
    u=sink;
    while(u!=source) //更新残量图 
    {
        e[prx[u]].flow-=ans;
        e[prx[u]^1].flow+=ans;
        u=pre[u];
    }
    return ans*dis[sink];
}
int solve()
{
    int cur=0;
    int ans=0;
    while(bfs())
    {
        cur+=dfs();
        if(cur>ans) ans=cur;
    } 
    return ans;
}
char map[MAXN][MAXN];
int mapnum[MAXN][MAXN];
int n,m;
struct Point
{
    int x,y,w;
    Point(){}
    Point(int xx,int yy,int ww)
    {
        x=xx,y=yy,w=ww;
    }
};
int vis[MAXN][MAXN];
int tur[4][2]={1,0,-1,0,0,1,0,-1};
void bfs_m(Point s)
{
    queue<Point> v;
    v.push(s);
    memset(vis,0,sizeof(vis));
    vis[s.x][s.y]=1;
    while(!v.empty())
    {
        Point u=v.front();
        v.pop();
        if(map[u.x][u.y]=='H') addtree(mapnum[s.x][s.y],mapnum[u.x][u.y],1,u.w);
        for(int i=0;i<4;i++)
        {
            Point pp=u;
            pp.x+=tur[i][0];
            pp.y+=tur[i][1];
            if(pp.x<0||pp.y<0||pp.x>=n||pp.y>=m||vis[pp.x][pp.y])continue;
            vis[pp.x][pp.y]=1;
            pp.w++;
            v.push(pp);
        }
    }

}
int main()
{

    while(scanf("%d%d",&n,&m)==2,n+m)
    {
        init();
        sink=1000,source=1;
        int cout=2;
        for(int i=0;i<n;i++)
        {
            scanf("%s",map[i]);
            for(int j=0;j<m;j++)
            {
                if(map[i][j]=='.') continue;
                mapnum[i][j]=cout++; //给点编号利于建图
                if(map[i][j]=='m')
                addtree(source,mapnum[i][j],1,0);
                else
                addtree(mapnum[i][j],sink,1,0);
            }
        }
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                if(map[i][j]=='m')
                bfs_m(Point(i,j,0)); //寻找每一个人对应所有家的最短路
            }
        }
        printf("%d\n",solve());
    }
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值