网络流相关知识参考: http://www.cnblogs.com/luweiseu/archive/2012/07/14/2591573.html

出处:優YoU http://blog.csdn.net/lyy289065406/article/details/6732762

大致题意:

给定一个N*M的地图,地图上有若干个man和house,且man与house的数量一致。man每移动一格需花费$1(即单位费用=单位距离),一间house只能入住一个man。现在要求所有的man都入住house,求最小费用。

解题思路:

费用流问题。

构图:

       把man作为一个顶点集合U,house作为另一个顶点集合V,把U中所有点到V中所有点连线,费用cost[u][v]为abs(△x)+abs(△y),反向弧费用cost[v][u]= -cost[u][v],容量cap[u][v]=1,构成一个多源多汇的二分图。

由于每一个多源多汇的网络流都必有一个与之对应的单源单汇的网络流,为了便于解题,由此构造一个超级源s和超级汇t,超级源s与U中所有点相连,费用cost[s][u]=0(这是显然的),容量cap[s][u]=1;V中所有点与超级汇t相连,费用cost[v][t]=0(这是显然的),容量cap[t][v]=1。

至于其他不连通的点,费用与容量均为0。容量为0的边,可以理解为饱和边,不再连通。而上述的所有边之所以容量初始化为1,是因为每间house只允许入住1个man。而与超级源(汇)相连的边的费用之所以为0,是为了现在所构造的单源单汇网络流最终所求的最小费用等于原来的多源多汇网络流的最小费用。

求解:

接下来的解题方法有关“最小费用最大流”,请未知的同学先去看看相关文献。

       其实题目所求的最小费用,就是最短距离。用spfa算法求上述二分图G的最短路径,该最短路径就是图G的所有增广链中费用最小的一条。比较该增广链上所有边的容量,最小的容量就是“可分配的最大流MaxFlow”。

       再者就是利用MaxFlow对增广链上的各条边的容量进行调整,正向弧容量减去MaxFlow,反向弧容量加上MaxFlow。然后该条增广链上各条边的费用分别乘以MaxFlow之和,就是第一个man到达合适的house所花费的最小费用。而图G经过调整,变成图G1。

       针对图G1再次使用spfa算法,找到第二条增广链.....重复上述算法,直到无法找到增广链为止,则得到的费用和就是所求。

       此种解题方法是先计算最小费用,然后再在保证费用最小的情况下,增大流量,最终达到求解目的。理论上,对于有n个man的图,可以找到n条增广链,即重复迭代spfa算法n次。若超过n次,则说明该图存在负权环,无解。但本题并无输出“无解”的要求,故可认为测试数据中不存在此类数据,因此在循环spfa算法时,不必计算次数,用while()足矣。

最后要注意的是:

       Discuss上很多人说用了栈空间定义的数组,提交会RE或TLE,数组开大之后才能AC,因此便怪罪于测试数据有误。

       其实不然。真正的原因是,题目所提及的N与M纯粹是地图的行数和列数,与man或house的个数无关,而我看了他们的代码,有部分同学却误以为N就是man的个数,导致出现RE。

       因此在此强调一下,题目的N不等于man的个数,建议大家先对地图的man个数进行数数,得到man的个数,然后再开堆空间(new函数);而非要开栈空间的同学也未尝不可,由于N与M的值均<=100,且man与house的个数一致,则认为man的个数上限为100*100/2=5000。

//Memory Time
//612K 63MS #include<iostream>
#include<memory.h>
#include<cmath>
#include<queue>
using namespace std; struct coordinate
{
int x,y;
}; class solve
{
public:
solve(int row,int col):R(row),C(col)
{
MinCost=0;
n=0; Input();
StructureBinaryMap(); pre=new int[2*n+2];
memset(pre,0,sizeof(int)*(2*n+2));
dist=new int[2*n+2];
vist=new bool[2*n+2]; while(spfa())
AddMaxFlow();
}
~solve()
{
cout<<MinCost<<endl; delete[] m;
delete[] H;
delete[] dist;
delete[] vist; for(int i=0;i<R;i++)
delete[] InMap[i]; for(int j=0;j<2*n+2;j++)
{
delete[] cost[j];
delete[] cap[j];
}
} int inf() const{return 0x7FFFFFFF;}
int min(int a,int b) {return a<b?a:b;} void Input(void);
void StructureBinaryMap(void); //构造二分图
bool spfa(void); //搜索从超级源到超级汇的最短路(即最小费用)的增广链
void AddMaxFlow(void); //增广链调整(曾流),计算最小费用 protected:
int R,C; //地图尺寸R*C ,
int n; //the number of man or Houses
int s,t; //s:超级源编号,t:超级汇编号
int MinCost; //最小花费 char** InMap; //输入的地图
coordinate* m; //记录所有man的坐标
coordinate* H; //记录所有House的坐标
int* pre; //最小费用流路径上,结点i的前驱结点为pre[i] int** cost; //两点间费用,其中超级源为0,1~n为man,n+1~2n为house,2n+1为超级汇
int** cap; //两点间容量,其中超级源为0,1~n为man,n+1~2n为house,2n+1为超级汇
int* dist; //超级源到各点的最短距离
bool* vist; //标记各点是否在队列
}; void solve::Input(void)
{
int i,j; InMap=new char*[R];
for(i=0;i<R;i++)
{
InMap[i]=new char[C];
for(j=0;j<C;j++)
{
cin>>InMap[i][j]; if(InMap[i][j]=='m') //检查'm'
n++; //得到man or house的数量
}
}
return;
} void solve::StructureBinaryMap(void)
{
int i,j;
int pm=0,pH=0; m=new coordinate[n+1];
H=new coordinate[n+1]; /*记录各个man与house的坐标*/ for(i=0;i<R;i++)
for(j=0;j<C;j++)
if(InMap[i][j]=='m')
{
m[++pm].x=i;
m[pm].y=j;
}
else if(InMap[i][j]=='H')
{
H[++pH].x=i;
H[pH].y=j;
} /*建立存储空间*/ cost=new int*[2*n+2];
cap=new int*[2*n+2];
for(i=0;i<2*n+2;i++)
{
cost[i]=new int[2*n+2];
cap[i]=new int[2*n+2]; memset(cost[i],0,sizeof(int)*(2*n+2)); //所有路径费用默认为0
memset(cap[i],0,sizeof(int)*(2*n+2)); //所有路径容量默认为0(饱和,不连通)
} /*初始化超级源s到各个man的容量*/ s=0;
for(i=1;i<=n;i++)
cap[s][i]=1; //容量默认为1 /*初始化各个man到house的距离(费用)及容量*/ for(i=1;i<=n;i++)
for(j=n+1;j<=2*n;j++)
{
cost[i][j]=abs(m[i].x-H[j-n].x)+abs(m[i].y-H[j-n].y); //man到house的费用为abs(△x)+abs(△y)
cost[j][i]=-cost[i][j]; //注意顺便构造负权边
cap[i][j]=1;
} /*初始化各个house到超级汇t的容量*/ t=2*n+1;
for(j=n+1;j<t;j++)
cap[j][t]=1; //容量默认为1 return;
} bool solve::spfa(void)
{
dist[s]=0;
for(int i=1;i<2*n+2;i++) //注意这里不能用memset(),memset是对字节为单位进行赋值
dist[i]=inf(); //若非置0,memset对char、bool以外的类型都会赋一个与期望完全不同的数值 memset(vist,false,sizeof(bool)*(2*n+2));
vist[s]=true; queue<int>q;
q.push(s); while(!q.empty())
{
int u=q.front(); //获取队头元素
for(int v=0;v<=t;v++)
{
if(cap[u][v] && dist[v]>dist[u]+cost[u][v]) //u->v容量未饱和,且能够松弛
{
dist[v]=dist[u]+cost[u][v];
pre[v]=u; //记录u的前驱结点 if(!vist[v])
{
q.push(v);
vist[v]=true;
}
}
}
q.pop(); //队头元素出队
vist[u]=false;
} if(dist[t]==inf()) //dist[t]没有被调整,说明已不存在增广链
return false; return true; //找到一条当前费用和最小的增广链
} void solve::AddMaxFlow(void)
{
int MaxFlow=inf(); //可分配最大流
int i; for(i=t;i!=s;i=pre[i]) //可分配最大流 为增广链上的最小容量边的容量
MaxFlow=min(MaxFlow,cap[pre[i]][i]); for(i=t;i!=s;i=pre[i]) //增广链上流量调整(增流)
{
cap[pre[i]][i]-=MaxFlow; //正向弧容量减去可分配最大流
cap[i][pre[i]]+=MaxFlow; //反向弧容量加上可分配最大流
MinCost+=cost[pre[i]][i]*MaxFlow; //最小费用=单位费用*流量
} return;
} int main(void)
{
int row,col;
while(cin>>row>>col && (row+col))
solve poj2195(row,col); return 0;
}

最小费用最大流 POJ2195-Going Home的更多相关文章

  1. POJ2195 Going Home —— 最大权匹配 or 最小费用最大流

    题目链接:https://vjudge.net/problem/POJ-2195 Going Home Time Limit: 1000MS   Memory Limit: 65536K Total ...

  2. POJ2195:Going Home (最小费用最大流)

    Going Home Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 26212   Accepted: 13136 题目链接 ...

  3. POJ 2195 Going Home 【最小费用最大流】

    题目链接:http://poj.org/problem?id=2195 Time Limit: 1000MS   Memory Limit: 65536K Total Submissions:2715 ...

  4. [板子]最小费用最大流(Dijkstra增广)

    最小费用最大流板子,没有压行.利用重标号让边权非负,用Dijkstra进行增广,在理论和实际上都比SPFA增广快得多.教程略去.转载请随意. #include <cstdio> #incl ...

  5. bzoj1927最小费用最大流

    其实本来打算做最小费用最大流的题目前先来点模板题的,,,结果看到这道题二话不说(之前打太多了)敲了一个dinic,快写完了发现不对 我当时就这表情→   =_=你TM逗我 刚要删突然感觉dinic的模 ...

  6. ACM/ICPC 之 卡卡的矩阵旅行-最小费用最大流(可做模板)(POJ3422)

    将每个点拆分成原点A与伪点B,A->B有两条单向路(邻接表实现时需要建立一条反向的空边,并保证环路费用和为0),一条残留容量为1,费用为本身的负值(便于计算最短路),另一条残留容量+∞,费用为0 ...

  7. HDU5900 QSC and Master(区间DP + 最小费用最大流)

    题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5900 Description Every school has some legends, ...

  8. P3381 【模板】最小费用最大流

    P3381 [模板]最小费用最大流 题目描述 如题,给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用. 输入输出格式 输入格式: 第一行 ...

  9. 【BZOJ-3876】支线剧情 有上下界的网络流(有下界有源有汇最小费用最大流)

    3876: [Ahoi2014]支线剧情 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 821  Solved: 502[Submit][Status ...

随机推荐

  1. WebRTC录音(2)-录音文件转换成WAV格式

    以下是源码,大路货,从网上找的. 但是,这个东西在MacOS上是有问题的,原因在最后,都是泪啊. #include <stdio.h> #include <string.h> ...

  2. ZOJ 1078 Palindrom Numbers

    原题链接 题目大意:判断一个数是不是palindrom.不限于十进制,可以在任何进制下判断. 解法:还好,数字的范围不大,int类型足够搞定.方法就是从2进制开始,先把数字转换成2进制,判断是否对称, ...

  3. Spring MVC 属性文件读取注入到静态字段

    目录(?)[-] servlet-contextxml configproperties 示例属性 ConfigInfo 对应的配置bean 使用   在项目中,有些参数需要配置到属性文件xxx.pr ...

  4. MySQL 参数autoReconnect=true 解决8小时连接失效

    <!--  dataSource加参数    处理mysql 8小时自动断开连接的问题 --> <property name="testWhileIdle" va ...

  5. MATLAB符号运算

    1.符号运算 使用MATLAB可以进行多项式乘除运算,也可以进行因式分解. 例1. 多项式乘除运算(x+3)3 >> syms x;>> expand((x+3)^3) ans ...

  6. php连接MongoDB

    一.安装php的mongo扩展 # wget https://github.com/mongodb/mongo-php-driver-legacy/archive/master.zip -O /usr ...

  7. UVALive-7303 Aquarium (最小生成树)

    题目大意:在nxm的方格中,每一个1x1的小方格中都有一堵沿对角线的墙,并且每堵墙都有一个坚固程度,这些墙将nxm的方格分割成了若干个区域.现在要拆除一些墙,使其变成一个区域. 题目分析:将区域视作点 ...

  8. jQuery 鼠标滚轮插件应用 mousewheel

    jQuery Mousewheel Plugin,用于添加跨浏览器的鼠标滚轮支持. mousewheel事件的处理函数有一点小小的变化,它除了第一个参数event 外,还接收到第二个参数delta. ...

  9. VS产生sdf和ipch文件太大处理方案

    方法: 工具-->选项-->文本编辑器-->C/C++-->高级-->回退位置,把始终使用回退位置设置为true,回退位置已在使用,不警告也设置为true,回退位置设置为 ...

  10. 谓词的使用 -ios

    #import <Foundation/Foundation.h> @interface Person : NSObject<NSCopying> @property(nona ...