题意:n个人,进n个房子,每走一格花费1美元,每个房子只能进一人,求所有人进房子的最小花费。   就是推箱子 箱子最短行走距离

这题无法用bfs做 !

用最小花费最大流

通过EK,Dinic,ISAP算法可以得到网络流图中的最大流,一个网络流图中最大流的流量max_flow是唯一的,但是达到最大流量max_flow时每条边上的流量分配f是不唯一的。 
    如果给网络流图中的每条边都设置一个费用cost,表示单位流量流经该边时会导致花费cost。那么在这些流量均为max_flow的流量分配f中,存在一个流量总花费最小的最大流方案。 
即 min{sum(cost(i, j)*f(i,j) | (i, j)属于方案f中的边, f(i,j)为 边(i,j)上的流量, f为某一个最大流方案}。此即为最小费用最大流

建图:

超级源点到所有人   每个人到每个房子均算出花费   所有房子到超级汇点  所有的边均为1     花费除了人到房子均为0

可当作最小花费最大流模板:

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <queue>
  4. #include <cstdlib>
  5. #include <algorithm>
  6. #define MAXN 200+10
  7. #define MAXM 80000+100
  8. #define INF 0x3f3f3f3f
  9. using namespace std;
  10. struct Edge
  11. {
  12. int from, to, cap, flow, cost, next;
  13. };
  14. Edge edge[MAXM];
  15. int head[MAXN], edgenum;
  16. int pre[MAXN], dist[MAXN];
  17. bool vis[MAXN];
  18. int N, M;
  19. int cost, flow;
  20. int sink, source;//超级源点 超级汇点
  21. void init()
  22. {
  23. edgenum = ;
  24. memset(head, -, sizeof(head));
  25. }
  26. void addEdge(int u, int v, int w, int c)
  27. {
  28. Edge E1 = {u, v, w, , c, head[u]};
  29. edge[edgenum] = E1;
  30. head[u] = edgenum++;
  31. Edge E2 = {v, u, , , -c, head[v]};
  32. edge[edgenum] = E2;
  33. head[v] = edgenum++;
  34. }
  35. int dis(int x1, int y1, int x2, int y2)
  36. {
  37. return abs(x1 - x2) + abs(y1 - y2);
  38. }
  39. struct Node
  40. {
  41. int x, y;
  42. };
  43. Node m[], H[];//存储字符坐标
  44.  
  45. bool SPFA(int s, int t)//寻找花销最少的路径
  46. {
  47. queue<int> Q;
  48. memset(dist, INF, sizeof(dist));
  49. memset(vis, false, sizeof(vis));
  50. memset(pre, -, sizeof(pre));
  51. dist[s] = ;
  52. vis[s] = true;
  53. Q.push(s);
  54. while(!Q.empty())
  55. {
  56. int u = Q.front();
  57. Q.pop();
  58. vis[u] = false;
  59. for(int i = head[u]; i != -; i = edge[i].next)
  60. {
  61. Edge E = edge[i];
  62. if(dist[E.to] > dist[u] + E.cost && E.cap > E.flow)//可以松弛 且 没有满流
  63. {
  64. dist[E.to] = dist[u] + E.cost;
  65. pre[E.to] = i;//记录前驱边 的编号
  66. if(!vis[E.to])
  67. {
  68. vis[E.to] = true;
  69. Q.push(E.to);
  70. }
  71. }
  72. }
  73. }
  74. return pre[t] != -;//可达返回true
  75. }
  76. void MCMF(int s, int t)
  77. {
  78. flow = ;//总流量
  79. cost = ;//总费用
  80.  
  81. while(SPFA(s, t))//每次寻找花销最小的路径
  82. {
  83. int Min = INF;
  84. //通过反向弧 在源点到汇点的最少花费路径 找最小增广流
  85. for(int i = pre[t]; i != -; i = pre[edge[i^].to])
  86. {
  87. Edge E = edge[i];
  88. Min = min(Min, E.cap - E.flow);
  89. }
  90. //增广
  91. for(int i = pre[t]; i != -; i = pre[edge[i^].to])
  92. {
  93. edge[i].flow += Min;
  94. edge[i^].flow -= Min;
  95. cost += edge[i].cost * Min;//增广流的花销
  96. }
  97. flow += Min;//流量累加
  98. }
  99. }
  100. int main()
  101. {
  102. int m_cnt;//m字符计数器
  103. int H_cnt;//H字符计数器
  104.  
  105. while(scanf("%d%d", &N, &M), N||M)
  106. {
  107. init();
  108.  
  109. m_cnt = H_cnt = ;
  110. char str[][];
  111. for(int i = ; i < N; i++)
  112. {
  113. scanf("%s", str[i]);
  114. for(int j = ; j < M; j++)
  115. {
  116. if(str[i][j] == 'm')
  117. {
  118. ++m_cnt;
  119. m[m_cnt].x = i;
  120. m[m_cnt].y = j;
  121. }
  122. if(str[i][j] == 'H')
  123. {
  124. ++H_cnt;
  125. H[H_cnt].x = i;
  126. H[H_cnt].y = j;
  127. }
  128. }
  129. }
  130. int k = m_cnt;//人数 或者 房子数
  131. sink = ;
  132. source = *k+;
  133. for(int i = ; i <= k; i++)
  134. {
  135. addEdge(sink, i, , );
  136. addEdge(i + k, source, , );
  137. for(int j = ; j <= k; j++)
  138. {
  139. int d = dis(H[i].x, H[i].y, m[j].x, m[j].y);
  140. addEdge(i, j + k, , d);
  141. }
  142. }
  143.  
  144. MCMF(sink, source);
  145. printf("%d\n", cost);
  146. }
  147. return ;
  148. }

随机推荐

  1. 【arc074e】RGB Sequence(动态规划)

    [arc074e]RGB Sequence(动态规划) 题面 atcoder 洛谷 翻译见洛谷 题解 直接考虑暴力\(dp\),设\(f[i][j][k][l]\)表示当前考虑到第\(i\)位,最后一 ...

  2. 洛谷 P1344 [USACO4.4]追查坏牛奶Pollutant Control 解题报告

    P1344 [USACO4.4]追查坏牛奶Pollutant Control 题目描述 你第一天接手三鹿牛奶公司就发生了一件倒霉的事情:公司不小心发送了一批有三聚氰胺的牛奶.很不幸,你发现这件事的时候 ...

  3. suoi44 核能显示屏 (cdq分治)

    首先二维树状数组肯定开不下 仿照二维树状数组的做法,如果有差分数组$d[i][j]=a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1]$,那么就有: $$sum[x][y] ...

  4. 结合NTLM中继和Kerberos委派攻击AD

    0x00 前言 在上个月我深入演讲了无约束委派之后,本文将讨论一种不同类型的Kerberos委派:基于资源的约束委派.本文的内容基于Elad Shamir的Kerberos研究,并结合我自己的NTLM ...

  5. Libre 6005 「网络流 24 题」最长递增子序列 / Luogu 2766 最长递增子序列问题(网络流,最大流)

    Libre 6005 「网络流 24 题」最长递增子序列 / Luogu 2766 最长递增子序列问题(网络流,最大流) Description 问题描述: 给定正整数序列x1,...,xn . (1 ...

  6. AC自动机——多个kmp匹配

    (并不能自动AC) 介绍: Aho-Corasick automaton,最经典的处理多个模式串的匹配问题. 是kmp和字典树的结合. 精髓与灵魂: ①利用trie处理多个模式串 ②引入fail指针. ...

  7. OpenStack 存储服务 Cinder存储节点部署NFS(十七)

    Cinder存储节点部署 1.安装软件包 yum install -y nfs-utils rpcbind 提示:早期版本安装portmap nfs-utils :包括基本的NFS命令与监控程序 rp ...

  8. python 爬虫 scrapy1_官网教程

    Python爬虫视频教程零基础小白到scrapy爬虫高手-轻松入门 https://item.taobao.com/item.htm?spm=a1z38n.10677092.0.0.482434a6E ...

  9. 鸟哥的Linux私房菜——第九章

    视频链接,推荐看B站 土豆网:http://www.tudou.com/programs/view/XmMDbjJHJC8 B站:http://www.bilibili.com/video/av966 ...

  10. angularJs的继承

    为什么要继承,本来是后端的概念,但是同样适用于前端开发.继承,无疑是将通用的东西抽取出来. 下面介绍的是angular的伪继承,就是说是通过继承scope这个变量来实现的.代码很简单,一行代码就可以. ...