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

http://poj.org/problem?id=2195

题意:有一个地图里面有N个人和N个家,每走一格的花费是1,问让这N个人分别到这N个家的最小花费是多少。

思路:通过这个题目学了最小费用最大流。最小费用最大流是保证在流量最大的情况下,使得费用最小。

建图是把S->人->家->T这些边弄上形成一个网络,边的容量是1(因为一个人只能和一个家匹配),边的费用是曼哈顿距离,反向边的费用是-cost。

算法的思想大概是通过SPFA找增广路径,并且找的时候费用是可以松弛的。当找到这样一条增广路就进行更新。注意这里的费用是单位流量的费用。反向边权为-cost是因为悔棋的时候费用要增加cost。

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <queue>
  4 #include <vector>
  5 #include <algorithm>
  6 using namespace std;
  7 #define N 205
  8 #define INF 0x3f3f3f3f
  9 struct node {
 10     int x, y;
 11     node () {}
 12     node (int x, int y) : x(x), y(y) {}
 13 };
 14 struct Edge {
 15     int cap, u, v, cost;
 16     Edge() {}
 17     Edge(int u, int v, int cap, int cost) : u(u), v(v), cap(cap), cost(cost) {}
 18 }edge[N*N];
 19 vector<node> p, h;
 20 vector<int> G[N];
 21 int tot, dis[N], inq[N], pre[N], S, T;
 22
 23 void AddEdge(int u, int v, int cap, int cost) {
 24     edge[tot] = Edge(u, v, cap, cost);
 25     G[u].push_back(tot++);
 26     edge[tot] = Edge(v, u, 0, -cost); // 表示反向增广(悔棋)的时候费用增加cost
 27     G[v].push_back(tot++);
 28 }
 29
 30 int CalDis(int x1, int y1, int x2, int y2) {
 31     return abs(x1 - x2) + abs(y1 - y2);
 32 }
 33
 34 bool SPFA() {
 35     memset(inq, 0, sizeof(inq));
 36     memset(dis, INF, sizeof(dis));
 37     queue<int> que; que.push(S);
 38     dis[S] = 0; inq[S] = 1;
 39     while(!que.empty()) {
 40         int u = que.front(); que.pop(); inq[u] = 0;
 41         for(int i = 0; i < G[u].size(); i++) {
 42             Edge &e = edge[G[u][i]];
 43             if(e.cap > 0 && dis[e.v] > e.cost + dis[u]) {
 44             // 当可以增广并且费用可以松弛的时候
 45                 dis[e.v] = e.cost + dis[u];
 46                 pre[e.v] = G[u][i]; // 记录路径
 47                 if(inq[e.v]) continue;
 48                 que.push(e.v); inq[e.v] = 1;
 49             }
 50         }
 51     }
 52     return dis[T] < INF; // 返回是否有增广路径
 53 }
 54
 55 void MFMC(int &mincost, int &maxflow) {
 56     int ans = 0, flow = INF, p;
 57     // 从汇点沿着此次增广的路径往回走,当找到源点的时候退出
 58     for(int u = T; u; u = edge[p].u) {
 59         p = pre[u]; // 找增广的流量
 60         if(edge[p].cap < flow) flow = edge[p].cap;
 61     }
 62     for(int u = T; u; u = edge[p].u) {
 63         p = pre[u];
 64         edge[p].cap -= flow; // 更新每条边的流量
 65         edge[p^1].cap += flow;
 66         ans += flow * edge[p].cost; // 费用 = 单位费用 * 流量
 67     }
 68     mincost += ans, maxflow += flow;
 69 }
 70
 71 int main() {
 72     int n, m;
 73     char s[105];
 74     while(scanf("%d%d", &n, &m), n + m) {
 75         p.clear(); h.clear();
 76         for(int i = 0; i < n; i++) {
 77             scanf("%s", s);
 78             for(int j = 0; j < m; j++) {
 79                 if(s[j] == ‘H‘) h.push_back(node(i, j));
 80                 if(s[j] == ‘m‘) p.push_back(node(i, j));
 81             }
 82         }
 83         tot = 0; int sz1 = p.size(), sz2 = h.size();
 84         S = 0, T = sz1 + sz2 + 1;
 85         for(int i = 0; i <= T; i++) G[i].clear();
 86         for(int i = 0; i < sz1; i++) // S到man
 87             AddEdge(S, i + 1, 1, 0);
 88         for(int i = 0; i < sz2; i++) // house到T
 89             AddEdge(i + 1 + sz1, T, 1, 0);
 90         for(int i = 0; i < sz1; i++) {
 91             for(int j = 0; j < sz2; j++) {
 92                 int c = CalDis(p[i].x, p[i].y, h[j].x, h[j].y);
 93                 AddEdge(i + 1, j + 1 + sz1, 1, c);
 94             }
 95         }
 96
 97         int mincost = 0, maxflow = 0;
 98         while(SPFA()) MFMC(mincost, maxflow);
 99         printf("%d\n", mincost);
100     }
101     return 0;
102 }
时间: 2025-01-03 15:08:14

POJ 2195:Going Home(最小费用最大流)的相关文章

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

题目链接:http://poj.org/problem?id=2195 题意:n*m的矩阵,地图上有若干个人(m)和房子(H),且人与房子的数量一致.man每移动一格费用为1,一个房子只能住一个人.现在要求所有的人出发,都入住房子,求最少话费. 思路:建立一个超级源点和汇点,源点与人相连费用为0,容量为1,人与房子相连,费用为人与房子的距离,容量为1,房子与汇点相连,费用为0,容量为1 #include <iostream> #include <cstdlib> #include

POJ 2195 地图的最小费用最大流

思路:这题刚开始看就知道是最小费用最大流了,因为求出最优嘛,而且要m,H要一一对应,所以不是二分图匹配就是最小费用最大流. 不过,刚开始还在想每个m与H之间的最小花费如何求,难道要用dfs搜索吗?这样想之后看了下题目给的时间是1000ms,然后就把dfs搜索m与H之间的最短距离排除了.然后想了想,其实尼玛太简单了,因为题目说了只能垂直与竖直的走,所以最短距离不就是两个横坐标相减与两个纵坐标相减之和嘛! 然后每对m与H之间都连边,流量为1(因为每对匹配不能重复),费用为它们之间的距离即花费:然后建

POJ 2195 - Going Home - [最小费用最大流][MCMF模板]

题目链接:http://poj.org/problem?id=2195 Time Limit: 1000MS Memory Limit: 65536K Description On a grid map there are n little men and n houses. In each unit time, every little man can move one unit step, either horizontally, or vertically, to an adjacent

POJ 2195 Going Home 最小费用最大流 尼玛,心累

D - Going Home Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit Status Practice POJ 2195 Appoint description: Description On a grid map there are n little men and n houses. In each unit time, every little man can mo

POJ 2516 Minimum Cost(最小费用最大流,坑题)

题目链接:http://poj.org/problem?id=2516 题意:有N个店,M个供货商,K种商品.已知供货商的仓库里每种商品的数量以及每种商品运送到每个店的费用,每个店铺对各种商品的需求数量,求最少话费. Input  第一行:N,M,K. 然后1 - N行,每行 K列 ,第I行第J个数代表 第I个店铺 需要第J种物品多少件. 然后 N+1 - M行  ,每行 K列 , 第I行第J个数代表 第I个供货商 有第J种物品多少件. 然后是K个矩阵  ,每个N行M列,第ji个矩阵的第i行第j

POJ 2516 Minimum Cost (最小费用最大流)

Minimum Cost Time Limit: 4000MS   Memory Limit: 65536K       Description Dearboy, a goods victualer, now comes to a big problem, and he needs your help. In his sale area there are N shopkeepers (marked from 1 to N) which stocks goods from him.Dearboy

POJ 3680: Intervals【最小费用最大流】

题目大意:你有N个开区间,每个区间有个重量wi,你要选择一些区间,使得满足:每个点被不超过K个区间覆盖的前提下,重量最大 思路:感觉是很好想的费用流,把每个区间首尾相连,费用为该区间的重量的相反数(由于要最大,所以是求最大费用最大流),容量为1,至于不超过K的限制,只要从源点到第一个点的流量为K就行,剩下每个相邻的点相连,费用为0,流量只要大于的等于K就可以(我取的正无穷) //poj3680 #include <stdio.h> #include <iostream> #incl

POJ 2135 Farm Tour [最小费用最大流]

题意: 有n个点和m条边,让你从1出发到n再从n回到1,不要求所有点都要经过,但是每条边只能走一次.边是无向边. 问最短的行走距离多少. 一开始看这题还没搞费用流,后来搞了搞再回来看,想了想建图不是很难,因为要保证每条边只能走一次,那么我们把边拆为两个点,一个起点和终点,容量是1,权重是这条路的长度.然后两个端点分别向起点连接容量是1权重是0的边,终点分别向两个端点连容量是1权重是0的边,从源点到1连容量为2权重为0的边,从n到汇点连容量为2权重为0的边. #include<stdio.h>

poj 2195 最小费用最大流模板

/*Source Code Problem: 2195 User: HEU_daoguang Memory: 1172K Time: 94MS Language: G++ Result: Accepted Source Code */ #include <iostream> #include <stdio.h> #include <queue> #include <math.h> #include <string.h> using namespa