• 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
    ", mincost);
    100     }
    101     return 0;
    102 }
  • 相关阅读:
    Hard 随机洗牌函数 @CareerCup
    Hard 随机选择subset @CareerCup
    Hard 计算0到n之间2的个数 @CareerCup
    Django admin进阶
    hdu 5630 Rikka with Chess
    PHP 表单验证
    PHP 表单验证
    PHP 表单验证
    PHP 表单验证
    PHP 表单处理
  • 原文地址:https://www.cnblogs.com/fightfordream/p/6208846.html
Copyright © 2020-2023  润新知