title: 網路流 date: 2018 07 31 22:01:22 tags: acm 演算法 網路流 概述 這篇博客主要是關於網路流的一些基本的知識點以及相應的模板,, 算了,,,還是先貼大佬的博客,,,暑假在補一下。。。。QAQ <! more 網路流 tan90,,,,,,, 習題 Pro ...
title: 網路流
date: 2018-07-31 22:01:22
tags:
- acm
- 演算法
網路流
概述
這篇博客主要是關於網路流的一些基本的知識點以及相應的模板,,
算了,,,還是先貼大佬的博客,,,暑假在補一下。。。。QAQ
網路流
tan90,,,,,,,
習題
Problem A: 養豬
Time Limit: 1 Sec Memory Limit: 128 MB
Description
AveryBoy喜歡玩LOL,但是他技術太菜,總是被別人噴“這麼菜玩什麼游戲,回家養豬去吧”。終於有一天,他被噴的受不了了,於是回家養豬。不過他家的養豬場在下雨天的時候總是被淹,所以他用讀書學來的知識設計了一套排水系統。他還設計了一套裝置,可以控制排水管道的水流流量。現在有n個排水管道,m個排水節點,問你從1到m的最大排水流量。
Input
有多組測試數據,對於每組測試數據,第一行是兩個整數n,m(0 <= n <= 200,2 <= m <= 200),分別表示排水管道數和排水節點數。之後n行每行包含3個整數,u,v,w(1<=u,v<=m,0<=w<=1e7,u!=v),表示從u到v的排水管道的水流流量是w。
Output
對於每種情況輸出一個整數,表示從1到m的最大排水流量。
Sample Input
5 4
1 2 40
1 4 20
2 4 20
2 3 30
3 4 10
Sample Output
50
模板題,,,直接套就行
#include <iostream>
#include <bits/stdc++.h>
#define ms(a , b) memset(a , b , sizeof(a))
using namespace std;
//前向星
typedef long long ll;
const int maxn = 1e4;
const int inf = 0x3f3f3f3f;
int n , m;
struct Edge
{
int to;
int next;
int w;
}edge[maxn << 1];
int head[maxn];
bool vis[maxn];
int cnt;
void init()
{
ms(head , -1);
cnt = 0;
}
void add(int u , int v , int w)
{
edge[cnt].to = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt++;
edge[cnt].to = u; //添加反向邊,,流量為零
edge[cnt].w = 0;
edge[cnt].next = head[v];
head[v] = cnt++;
}
int step[maxn];
bool bfs(int s , int t)
{
ms(step , -1);
step[s] = 0;
queue<int> q;
q.push(s);
while (!q.empty())
{
int u = q.front();q.pop();
for (int i = head[u]; i != -1; i = edge[i].next)
{
if (step[edge[i].to] == -1 && edge[i].w > 0)
{
step[edge[i].to] = step[u] + 1;
q.push(edge[i].to);
if (edge[i].to == t) return true;
}
}
}
return step[t] != -1;
}
int dfs(int s , int t , int f)
{
if (s == t || !f) return f;
int flow = 0;
for (int i = head[s]; i != -1; i = edge[i].next)
{
if (step[s] + 1 == step[edge[i].to] && edge[i].w > 0)
{
int d = dfs(edge[i].to , t , min(edge[i].w , f));
if (d > 0)
{
edge[i].w -= d;
edge[i ^ 1].w += d;
flow += d; //累加當前節點的某條路徑的合適流量
f -= d; //當前節點的容量減去某條路徑的合適流量
if (f == 0) break; //如果當前節點的容量用完,說明無法再通過任何流量
}
}
}
if (flow == 0) step[s] = inf; //如果當前節點無任何流量通過,取消標記
return flow;
}
int Dinic(int s , int t)
{
int flow = 0;
while (bfs(s , t))
{
flow += dfs(s , t , inf);
}
return flow;
}
int main()
{
//ios_base::sync_with_stdio(0);
while (~scanf("%d%d", &n , &m))
{
int u , v , w;
init();
for (int i = 1; i <= n; i++)
{
//cin >> u >> v >> w;
scanf("%d%d%d" , &u , &v , &w);
add(u , v , w);
}
printf("%d\n" , Dinic(1 , m));
//cout << "Case " << k++ << ": " << ans << endl;
}
return 0;
}
學長用的鄰接表存的,,,
學長的代碼:
// hdu 1532
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define PB push_back
const int INF = 0x3f3f3f3f;
const int maxn = 205;
int n,m;
struct Edge{
int to,cap,idx;
Edge(){}
Edge(int to,int cap,int idx):to(to),cap(cap),idx(idx){}
};
vector<Edge> V[maxn];
bool vis[maxn];
void add_edge(int u,int v,int w)
{
V[u].PB(Edge(v,w,V[v].size()));
V[v].PB(Edge(u,0,V[u].size()-1));
}
int dfs(int s,int t,int f)
{
if(s==t) return f;
vis[s]=true;
for(int i=0;i<V[s].size();i++)
{
Edge &cur = V[s][i];
if(!vis[cur.to] && cur.cap>0)
{
int tmp = dfs(cur.to,t,min(f,cur.cap));
if(tmp>0)
{
cur.cap -= tmp;
V[cur.to][cur.idx].cap += tmp;
return tmp;
}
}
}
return 0;
}
int Ford_Fulkerson(int s,int t)
{
int res = 0;
while(true)
{
memset(vis,false,sizeof(vis));
int flow = dfs(s,t,INF);
if(flow==0) return res;
res += flow;
}
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
for(int i=1;i<=m;i++) V[i].clear();
int u,v,w;
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&u,&v,&w);
add_edge(u,v,w);
}
printf("%d\n",Ford_Fulkerson(1,m));
}
return 0;
}
Problem B: 最大流
Time Limit: 1 Sec Memory Limit: 128 MB
Description
如題,給你一個容量網路,請你找出最大流。
Input
第一行輸入包含一個整數T,表示測試用例的數量。
對於每個測試用例,第一行包含兩個整數N和M,表示圖中頂點和邊的數量。(2 <= N <= 15,0 <= M <= 1000)
接下來的M行,每行包含三個整數X,Y和C,表示從X到Y有一個邊,它的容量是C.(1 <= X,Y <= N,1 <= C <= 1000)
Output
對於每個測試用例,您應該輸出從源點1到匯點N的最大流量。
Sample Input
2
3 2
1 2 1
2 3 1
3 3
1 2 1
2 3 1
1 3 1
Sample Output
Case 1: 1
Case 2: 2
同樣是模板題,,,不過剛開始我套fk的模板一直tle就換了dinic演算法
#include <iostream>
#include <bits/stdc++.h>
#define ms(a , b) memset(a , b , sizeof(a))
using namespace std;
//前向星
const int maxn = 1e4;
const int inf = 0x3f3f3f3f;
int n , m;
struct Edge
{
int to;
int next;
int w;
}edge[maxn << 1];
int head[maxn];
bool vis[maxn];
int cnt;
void init()
{
ms(head , -1);
cnt = 0;
}
void add(int u , int v , int w)
{
edge[cnt].to = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt++;
edge[cnt].to = u; //添加反向邊,,流量為零
edge[cnt].w = 0;
edge[cnt].next = head[v];
head[v] = cnt++;
}
int step[maxn];
bool bfs(int s , int t)
{
ms(step , -1);
step[s] = 0;
queue<int> q;
q.push(s);
while (!q.empty())
{
int u = q.front();q.pop();
for (int i = head[u]; i != -1; i = edge[i].next)
{
if (step[edge[i].to] == -1 && edge[i].w > 0)
{
step[edge[i].to] = step[u] + 1;
q.push(edge[i].to);
if (edge[i].to == t) return true;
}
}
}
return step[t] != -1;
}
int dfs(int s , int t , int f)
{
if (s == t || !f) return f;
int flow = 0;
for (int i = head[s]; i != -1; i = edge[i].next)
{
if (step[s] + 1 == step[edge[i].to] && edge[i].w > 0)
{
int d = dfs(edge[i].to , t , min(edge[i].w , f));
if (d > 0)
{
edge[i].w -= d;
edge[i ^ 1].w += d;
flow += d; //累加當前節點的某條路徑的合適流量
f -= d; //當前節點的容量減去某條路徑的合適流量
if (f == 0) break; //如果當前節點的容量用完,說明無法再通過任何流量
}
}
}
if (flow == 0) step[s] = inf; //如果當前節點無任何流量通過,取消標記
return flow;
}
int Dinic(int s , int t)
{
int flow = 0;
while (bfs(s , t))
{
flow += dfs(s , t , inf);
}
return flow;
}
int main()
{
//ios_base::sync_with_stdio(0);
int t;scanf("%d" , &t);
//cin >> t;
int k = 1;
while (t--)
{
//cin >> n >> m;
scanf("%d%d", &n , &m);
int u , v , w;
init();
for (int i = 1; i <= m; i++)
{
//cin >> u >> v >> w;
scanf("%d%d%d" , &u , &v , &w);
add(u , v , w);
}
printf("Case %d: %d\n" , k++ , Dinic(1 , n));
//cout << "Case " << k++ << ": " << ans << endl;
}
return 0;
}
學長的代碼:
// hdu 3549
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define PB push_back
const int INF = 0x3f3f3f3f;
const int maxn = 20;
int c[maxn][maxn],f[maxn][maxn],p[maxn],a[maxn];
int m,n;
int bfs()
{
queue<int> q;
memset(p,-1,sizeof(p));
memset(a,0,sizeof(a));
a[1] = INF;
q.push(1);
while(!q.empty())
{
int u = q.front();q.pop();
for(int i=1;i<=n;i++)
{
if(!a[i] && c[u][i]>f[u][i])
{
p[i] = u;
q.push(i);
a[i] = min(a[u],c[u][i]-f[u][i]);
}
}
if(a[n]) break;
}
if(!a[n]) return 0;
for(int u=n;u!=1;u=p[u])
{
f[p[u]][u] += a[n];
f[u][p[u]] -= a[n];
}
return a[n];
}
int Edmonds_Karp()
{
int res = 0;
while(true)
{
int tmp = bfs();
if(tmp==0) return res;
res += tmp;
}
}
int main()
{
int t;
scanf("%d",&t);
for(int ca=1;ca<=t;ca++)
{
scanf("%d%d",&n,&m);
memset(c,0,sizeof(c));
memset(f,0,sizeof(f));
int u,v,w;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
c[u][v] += w;
}
int max_flow=Edmonds_Karp();
printf("Case %d: %d\n",ca,max_flow);
}
return 0;
}
Problem C: 房子和車
Time Limit: 1 Sec Memory Limit: 128 MB
Description
華中農業大學總共有n個老師,f種房子和d種車(1 <= n,f,d <= 200)。每個老師都有自己喜歡的一些房子和車的類型,現在要你把這些房子和車分配給這n個老師,每個老師只分配一套房子和一輛車。問你最多能使多少個老師滿意對應的分配。
Input
有多組測試數據,每組測試數據第一行是3個正整數,n,f,d,表示老師個數,房子種數,車子種數。
第二行包含f個整數,其中第i個數表示第i種房子的個數。
第三行包含d個整數,其中第i個數表示第i種車子的個數。
之後n行,每行包含長度為f的字元串,其中第i行第j個字元表示第i個老師是否喜歡第j種房子,‘Y’表示喜歡,‘N’表示不喜歡。
之後n行,每行包含長度為d的字元串,其中第i行第j個字元表示第i個老師是否喜歡第j種車子,‘Y’表示喜歡,‘N’表示不喜歡。
Output
對於每組測試數據,輸出一個整數,表示最大的老師滿意的個數。
Sample Input
4 3 3
1 1 1
1 1 1
YYN
NYY
YNY
YNY
YNY
YYN
YYN
NNY
Sample Output
3
這道題主要是將題目所給的信息用圖描述出來,,,老師的處理是一分為二即可,,,
我的代碼:
#include <iostream>
#include <bits/stdc++.h>
#define ms(a , b) memset(a , b , sizeof(a))
using namespace std;
//前向星
typedef long long ll;
const int maxn = 1e5;
const int maxm = 1e3 + 10;
const int inf = 0x3f3f3f3f;
int n , f , d;
int home[maxm];
int car[maxm];
struct Edge
{
int to;
int next;
int w;
}edge[maxn << 1];
int head[maxn];
bool vis[maxn];
int cnt;
void init()
{
ms(head , -1);
cnt = 0;
}
void add(int u , int v , int w)
{
edge[cnt].to = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt++;
edge[cnt].to = u; //添加反向邊,,流量為零
edge[cnt].w = 0;
edge[cnt].next = head[v];
head[v] = cnt++;
}
int step[maxn];
bool bfs(int s , int t)
{
ms(step , -1);
step[s] = 0;
queue<int> q;
q.push(s);
while (!q.empty())
{
int u = q.front();q.pop();
for (int i = head[u]; i != -1; i = edge[i].next)
{
if (step[edge[i].to] == -1 && edge[i].w > 0)
{
step[edge[i].to] = step[u] + 1;
q.push(edge[i].to);
if (edge[i].to == t) return true;
}
}
}
return step[t] != -1;
}
int dfs(int s , int t , int f)
{
if (s == t || !f) return f;
int flow = 0;
for (int i = head[s]; i != -1; i = edge[i].next)
{
if (step[s] + 1 == step[edge[i].to] && edge[i].w > 0)
{
int d = dfs(edge[i].to , t , min(edge[i].w , f));
if (d > 0)
{
edge[i].w -= d;
edge[i ^ 1].w += d;
flow += d; //累加當前節點的某條路徑的合適流量
f -= d; //當前節點的容量減去某條路徑的合適流量
if (f == 0) break; //如果當前節點的容量用完,說明無法再通過任何流量
}
}
}
if (flow == 0) step[s] = inf; //如果當前節點無任何流量通過,取消標記
return flow;
}
int Dinic(int s , int t)
{
int flow = 0;
while (bfs(s , t))
{
flow += dfs(s , t , inf);
}
return flow;
}
int main()
{
//ios_base::sync_with_stdio(0);
while (~scanf("%d%d%d", &n , &f , &d))
{
init();
for (int i = 1; i <= f; i++)
scanf("%d" , &home[i]);
for (int i = 1; i <= d; i++)
scanf("%d" , &car[i]);
int s = 0; //超級原點
int t = f + n + n + d + 1; //匯點
for (int i = 1; i <= f; i++)
add(0 , i , home[i]); //原點到每個房子的點建邊
char str[maxm];
for (int i = 1; i <= n; i++)
{
scanf("%s" , str);
for (int j = 1; j <= f; j++)
{
if (str[j - 1] == 'Y')
add(j , i + f, 1); //老師滿意的和對應的房子連接,,,流量為1
}
add(i + f , f + n + i , 1); //分離出兩個老師的點,,,同一個老師之間流量為1
}
for (int i = 1; i <= n; i++)
{
scanf("%s" , str);
for (int j = 1; j <= d; j++)
if (str[j - 1] == 'Y')
add(f + n + i , f + n + n + j , 1);//第二個老師的點和車子建邊,,,流量為1
}
for (int i = 1; i <= d; i++)
add(f + n + n + i , t , car[i]); //匯點和車子之間建邊,
printf("%d\n" , Dinic(s , t));
}
return 0;
}
add(f + n + n + i , t , 1);
//----------
//這個在處理點之間的關係和我的不同,,,一個是老師分開另一個是分開的老師相鄰就是下麵這個
int s = 0;
int t = f + n + n + d + 1;
for (int i = 1; i <= f; i++)
add(0 , i , home[i]);
char str[maxm];
for (int i = 1; i <= n; i++)
{
scanf("%s" , str);
for (int j = 1; j <= f; j++)
{
if (str[j - 1] == 'Y')
add(j , f + 2 * i - 1 , 1);
}
add(f + 2 * i - 1 , f + 2 * i , 1);
}
for (int i = 1; i <= n; i++)
{
scanf("%s" , str);
for (int j = 1; j <= d; j++)
if (str[j - 1] == 'Y')
add(f + 2 * i , f + n + n + j , 1);
}
for (int i = 1; i <= d; i++)
add(f + n + n + i , t , car[i]);
printf("%d\n" , Dinic(s , t));
學長的代碼:
// hdu 4292
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#define INF 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=1000+50;
const int M=1e6+50;
struct node
{
node() {};
node(int tv,int tw,int tnext)
{
v=tv,w=tw,next=tnext;
};
int v,w,next;
} e[M];
int first[N],vis[N],dis[N],tot;
void add_edge(int u,int v,int w)
{
e[tot]=node(v,w,first[u]);
first[u]=tot++;
e[tot]=node(u,0,first[v]);
first[v]=tot++;
}
int bfs(int s,int t)
{
mem(vis,0);
mem(dis,0);
queue<int>q;
q.push(s);
vis[s]=1;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=first[u]; ~i; i=e[i].next)
{
if(!vis[e[i].v]&&e[i].w>0)
{
vis[e[i].v]=1;
dis[e[i].v]=dis[u]+1;
q.push(e[i].v);
}
}
}
return dis[t];
}
int dfs(int u,int t,int flow)
{
if(u==t)return flow;
for(int i=first[u]; ~i; i=e[i].next)
{
if(dis[e[i].v]==dis[u]+1&&e[i].w>0)
{
int dd=dfs(e[i].v,t,min(e[i].w,flow));
if(dd)
{
e[i].w-=dd;
e[i^1].w+=dd;
return dd;
}
}
}
dis[u]=0;
return 0;
}
int Dinic(int s,int t)
{
int ans=0,flow;
while(bfs(s,t))
{
while(flow=dfs(s,t,INF))
ans+=flow;
}
return ans;
}
void init()
{
mem(first,-1);
tot=0;
}
int a[N],b[N];
char s[N];
int main()
{
int n,f,d;
while(~scanf("%d%d%d",&n,&f,&d))
{
init();
for(int i=1; i<=f; i++) scanf("%d",&a[i]);
for(int i=1; i<=d; i++) scanf("%d",&b[i]);
for(int i=1; i<=n; i++)
{
add_edge(f+2*i-1,f+2*i,1);
scanf("%s",s+1);
for(int j=1; j<=f; j++)
if(s[j]=='Y')
add_edge(j,f+2*i-1,1);
}
for(int i=1; i<=n; i++)
{
scanf("%s",s+1);
for(int j=1; j<=d; j++)
if(s[j]=='Y')
add_edge(f+2*i,f+2*n+j,1);
}
for(int i=1; i<=f; i++) add_edge(0,i,a[i]);
for(int i=1; i<=d; i++) add_edge(2*n+f+i,2*n+f+d+1,b[i]);
printf("%d\n",Dinic(0,2*n+f+d+1));
}
return 0;
}
Problem D: 回家
Time Limit: 5 Sec Memory Limit: 128 MB
Description
在網格地圖上有n個人和n個房子。在每個單位時間內,每個人都可以水平或垂直移動到相鄰點。對於每個人,你需要為他移動的每一步支付1美元的旅行費,直到他進入房子。每個房子只能容納一個人。現在問你所有人都回到房子所需要的最少費用是多少?輸入是一個網格圖,‘.’表示空地,‘H’表示房子,‘m’表示人。
Input
有多組測試數據,對於每組測試數據第一行是兩個正整數n,m表示地圖的行和列(2<=n,m<=100)。地圖上有相同數量的房子和人,房子最多不超過100。輸入以n=0,m=0結束。
Output
對於每組測試數據輸出一個整數,表示所有人都回到房子所需的最小費用。
Sample Input
2 2
.m
H.
5 5
HH..m
.....
.....
.....
mm..H
7 8
...H....
...H....
...H....
mmmHmmmm
...H....
...H....
...H....
0 0
Sample Output
2
10
28
這道題自己做的時候被網上的模板坑了一手,,,一直tle,,,換模板就行了,,,
主要思路是,先將人房找到,,,計算出每一個人和所有房子直接的距離,,這個距離也叫曼哈頓距離,,,然後人房直接建邊,,再弄一個超級原點和匯點求原點和會頂啊直接的最小費用的最大流就可以了,,,,
#include <iostream>
#include <bits/stdc++.h>
#define ms(a , b) memset(a , b , sizeof(a))
using namespace std;
//前向星
typedef long long ll;
const int maxn = 1e3 + 5;
const int maxm = 1e3 + 5;
const int inf = 0x3f3f3f3f;
int n , m;
char mp[maxm][maxm];
struct Man
{
int x , y;
}man[maxn];
int cnt_man;
struct Home
{
int x , y;
}home[maxn];
int cnt_home;
struct Edge
{
int v;
int u;
int next;
int cap;
int cost;
Edge(){}
Edge(int u , int v, int cap , int cost , int next):u(u) , v(v) , cap(cap) , cost(cost) , next(next){}
}edge[maxn << 7];
int head[maxn];
int cnt;
void init()
{
ms(head , -1);
cnt = 0;
cnt_home = 1;
cnt_man = 1;
}
void add(int from , int to , int cap , int cost)
{
edge[cnt] = Edge(from , to , cap , cost , head[from]);
head[from] = cnt++;
edge[cnt] = Edge(to , from , 0 , -cost , head[to]);
head[to] = cnt++;
}
int dis[maxn << 1];
int pe[maxn << 1];
bool vis[maxn << 1];
bool spfa(int s , int t)
{
ms(dis , inf);
ms(vis , false);
ms(pe , -1);
dis[0] = 0;
vis[s] = true;
queue<int> q;
q.push(s);
while(!q.empty())
{
int u = q.front();q.pop();
vis[u] = false;
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
int cost = edge[i].cost;
if (edge[i].cap > 0 && dis[v] > dis[u] + cost)
{
dis[v] = dis[u] + cost;
pe[v] = i;
if (!vis[v])
{
vis[v] = true;
q.push(v);
}
}
}
}
if (dis[t] == inf) return false;
return true;
}
int min_cost_flow(int s , int t , int f)
{
int res = 0;
while (spfa(s , t))
{
int flow = inf;
for (int i = pe[t]; i != -1; i = pe[edge[i].u])
{
flow = min(flow , edge[i].cap);
}
f -= flow;
if (f < 0) break;
for (int i = pe[t]; i != -1; i = pe[edge[i].u])
{
edge[i].cap -= flow;
edge[i ^ 1].cap += flow;
}
res += flow * dis[t];
}
return res;
}
int main()
{
//ios_base::sync_with_stdio(0);
while (~scanf("%d%d", &n , &m) && n && m)
{
init();
char str[maxm];
for (int i = 1; i <= n; i++) //存圖
{
scanf("%s" , str);
for (int j = 1; j <= m; j++)
mp[i][j] = str[j - 1];
}
for (int i = 1; i <= n; i++) //人房分離,,記錄坐標
{
for (int j = 1; j <= m; j++)
{
if (mp[i][j] == 'H')
{
home[cnt_home].x = i;
home[cnt_home++].y = j;
}
else if (mp[i][j] == 'm')
{
man[cnt_man].x = i;
man[cnt_man++].y = j;
}
}
}
for (int i = 1; i <= cnt_man - 1; i++)
{
for (int j = 1; j <= cnt_home - 1; j++)
{ //算出每一個人對於所有房子的距離,,(曼哈頓距離),,,
int w = (int)fabs(man[i].x - home[j].x) + (int)fabs(man[i].y - home[j].y);
add(i , j + cnt_man - 1 , 1 , w); //人房之間連邊,,,流量為剛剛的值
}
}
int t = cnt_home; //匯點
t *= 2;
t--;
for (int i = 1; i <= cnt_man - 1; i++) //超級原點和每個人建邊,,流量為0
add(0 , i , 1 , 0);
for (int i = cnt_man; i <= t - 1; i++) //房子和匯點建邊
add(i , t , 1 , 0);
printf("%d\n" , min_cost_flow(0 , t , t + 1));
}
return 0;
}
學長的代碼:
// hdu 1533
#include <cstdio>
#include <queue>
#include <cstring>
#include <cstdlib>
using namespace std;
typedef long long ll;
#define PB push_back
const int INF = 0x3f3f3f3f;
const int maxn = 1005;
typedef pair<int,int> P;
char mp[105][105];
int dist[maxn<<1],pe[maxn<<1],head[maxn<<1];
bool vis[maxn<<1];
int n,m,tot;
struct Edge{
int u,v,cap,cost,next;
Edge(){}
Edge(int u,int v,int cap,int cost,int next):u(u),v(v),cap(cap),cost(cost),next(next){}
}edge[maxn<<7];
void add_edge(int from,int to,int cap,int cost)
{
edge[tot] = Edge(from,to,cap,cost,head[from]);
head[from] = tot++;
edge[tot] = Edge(to,from,0,-cost,head[to]);
head[to] = tot++;
}
bool SPFA(int s,int t)
{
memset(dist,INF,sizeof(dist));
memset(vis,false,sizeof(vis));
memset(pe,-1,sizeof(pe));
dist[s]=0;
vis[s]=true;
queue<int> q;
q.push(s);
while(!q.empty())
{
int u = q.front();q.pop();
vis[u] = false;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v = edge[i].v;
int cost = edge[i].cost;
if(edge[i].cap>0 && dist[v]>dist[u]+cost)
{
dist[v] = dist[u]+cost;
pe[v] = i;
if(!vis[v])
{
vis[v]=true;
q.push(v);
}
}
}
}
if(dist[t]==INF) return false;
else return true;
}
int min_cost_flow(int s,int t,int f)
{
int res = 0;
while(SPFA(s,t))
{
int flow = INF;
for(int i=pe[t];i!=-1;i=pe[edge[i].u])
{
flow = min(flow,edge[i].cap);
}
f -= flow;
if(f<0) break;
for(int i=pe[t];i!=-1;i=pe[edge[i].u])
{
edge[i].cap -= flow;
edge[i^1].cap += flow;
}
res += flow*dist[t];
}
return res;
}
int dis(P a,P b)
{
return abs(a.first-b.first)+abs(a.second-b.second);
}
int main()
{
while(~scanf("%d%d",&n,&m) && (n!=0 && m!=0))
{
int num1=0,num2=0;
P man[maxn],hos[maxn];
for(int i=1;i<=n;i++)
{
scanf("%s",mp[i]);
for(int j=0;j<m;j++)
{
if(mp[i][j]=='m')
man[++num1] = P(i,j+1);
if(mp[i][j]=='H')
hos[++num2] = P(i,j+1);
}
}
int s=0,t=num1+num2+1;
memset(head,-1,sizeof(head));
tot=0;
for(int i=1;i<=num1;i++)
add_edge(0,i,1,0);
for(int i=1;i<=num2;i++)
add_edge(num1+i,t,1,0);
for(int i=1;i<=num1;i++)
{
for(int j=1;j<=num2;j++)
{
add_edge(i,num1+j,1,dis(man[i],hos[j]));
}
}
printf("%d\n",min_cost_flow(s,t,num1));
}
return 0;
}
鴿~~~~~~~~~~~~~~