网络流相关知识参考: 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. maven出错The folder is already a source folder

    右键build path -> configure build path -> source ,选择 src/main/java.src/test/java删除,然后再新建.

  2. PHP实例开发(2)PHP通过mail()或Socket发邮件

    PHP通过mail()或Socket发邮件 1.PHP中发送邮件的方法 PHP发送邮件是“非常的简单” 因为他提供了mail()函数直接发送,但这也继 register globals 成为了对初学者 ...

  3. HTML DOM部分---做竖向横向的下拉导航 下拉菜单 图片轮播(圆点、箭头) 选项卡 进度条;

    1,竖向下拉导航 鼠标单击打开 再打击关闭 <style> *{ margin:0px auto; padding:0px;} div{ width:100px; height:50px; ...

  4. 三国游戏 2010年NOIP全国联赛普及组

    题目描述 Description 小涵很喜欢电脑游戏,这些天他正在玩一个叫做<三国>的游戏. 在游戏中,小涵和计算机各执一方,组建各自的军队进行对战.游戏中共有N 位武将(N 为偶数且不小 ...

  5. Codeforces Round #144 (Div. 2)

    A. Perfect Permutation 奇偶对调. B. Non-square Equation \(s(x)\)不超过200,根据求根公式计算\(x\). C. Cycles 每次新增点时都和 ...

  6. 关于setInterval()里的this和细节

    setInterval(fn,t);里的fn中,要使用外部类的this,则需要先将this保存起来,再使用保存的this,不能直接使用this,里面的this是指向window对象,记住setInte ...

  7. <初级程序员> git 的初级使用

    作为程序员,Git 是一个很好的代码管理工具.Git 是一个版本控制系统,主要的作用就是记录代码的修改过程,有效的追踪文件的变化.当代码出现错误的时候可以很容易的恢复到之前的状态,不管对于个人开发还是 ...

  8. C++@子类类型转换为父类类型

    static_cast(*this) to a base class create a temporary copy. class Window { // base class public: vir ...

  9. [BZOJ 3622]已经没有什么好害怕的了

    世萌萌王都拿到了,已经没有什么好害怕的了——    (作死) 笑看哪里都有学姐,真是不知说什么好喵~ 话说此题是不是输 0 能骗不少分啊,不然若学姐赢了,那么有头的学姐还能叫学姐吗?  (作大死) 这 ...

  10. 自然语言处理1——语言处理与Python(内含纠错)

    学习Python自然语言处理,记录一下学习笔记. 运用Python进行自然语言处理需要用到nltk库,关于nltk库的安装,我使用的pip方式. pip nltk 或者下载whl文件进行安装.(推荐p ...