# 題目: UVa 10888 - Warehouse

# 題目說明

給一個 N * M 的圖,圖上有 4 種符號 BX.#
你的目標是要找到使所有 B 移動到 X (不可經過 # ) 的最小距離


INPUT:
先輸入一個整數 T ,代表測資數
每筆測資輸入兩個整數 NM
接著輸入 N * M 個字元


OUTPUT:
輸出使所有 B 移動到 X (不可經過 # ) 的最小距離

# 解題方法

此題的解題概念為 Minimum cost Maximum flow
Maximum flow 的部分使用 Ford Fulkerson 演算法
Minimum cost 則使用佇列最佳化的 bellman ford 演算法,又稱 Shortest Path Faster Algorithm ,簡稱 SPFA

先利用 BFS 找到每個 BX 之間的最短距離
之後建表

  1. 起點 S 連接到每個 Bcost 為 0、 capacity 為 1
  2. 每個 B 連到能夠走到的 Xcost 為此 BX 的最短距離、 capacity 為 1
  3. 每個 X 連接到終點 Tcost 為 0、 capacity 為 1

之後跑 Minimum cost Maximum flow 的演算法即可

Minimum cost Maximum flow 在以下兩篇已說明過,不再贅述
UVa 10594 - Data Flow
UVa 10806 - Dijkstra, Dijkstra

# 參考程式碼

#include <iostream>
#include <vector>
#include <queue>
#include <climits>
using namespace std;
static auto fast_io = []
{
	ios::sync_with_stdio(false);
	cout.tie(nullptr);
	cin.tie(nullptr);
	return 0;
}();
pair<int, int> Move[] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} };
int T, N, M;
int x_cnt, b_cnt;
int _s = 0, _t;
vector< vector<char> > G;
vector< vector<int> > B;
vector< vector<int> > X;
vector< vector<int> > dis;
vector< vector<int> > edge;
vector< vector<int> > cost;
vector< vector<int> > capacity;
vector< vector<int> > net;
vector<int> d;
vector<int> p;
void init()
{
	x_cnt = b_cnt = 0;
	G.assign(200, vector<char>(200));
	B.assign(200, vector<int>(200, INT_MAX));
	X.assign(200, vector<int>(200, 0));
	edge.assign(200, vector<int>());
	cost.assign(200, vector<int>(200, 0));
	capacity.assign(200, vector<int>(200, 0));
	net.assign(200, vector<int>(200, 0));
	p.assign(200, 0);
}
void bfs(int i, int j, int cur)
{
	dis.assign(200, vector<int>(200, INT_MAX / 2));
	dis[i][j] = 0;
	queue< pair<int, int> > Q;
	Q.push({ j, i });
	while (!Q.empty())
	{
		auto [x, y] = Q.front();
		Q.pop();
		if (G[y][x] == 'X') B[cur][X[y][x]] = dis[y][x];
		for (int i = 0; i < 4; ++i)
		{
			int newx = x + Move[i].first;
			int newy = y + Move[i].second;			
			if (newx >= 0 && newx < M && newy >= 0 && newy < N && G[newy][newx] != '#' && dis[newy][newx] > dis[y][x] + 1)
			{			
				dis[newy][newx] = dis[y][x] + 1;
				Q.push({ newx, newy });
			}
		}
	}
}
void read()
{
	cin >> N >> M;
	for (int i = 0; i < N; ++i) for (int j = 0; j < M; ++j)
	{
		cin >> G[i][j];
		if (G[i][j] == 'X') X[i][j] = x_cnt++;
	}
	for (int i = 0; i < N; ++i) for (int j = 0; j < M; ++j)
		if (G[i][j] == 'B') bfs(i, j, b_cnt++);
	_t = x_cnt + b_cnt + 1;
	for (int i = 0; i < b_cnt; ++i)
	{
		edge[_s].emplace_back(i + 1);
		edge[i + 1].emplace_back(_s);
		capacity[_s][i + 1] = 1;
		capacity[i + 1][_s] = 0;
		cost[_s][i + 1] = 0;
		cost[i + 1][_s] = 0;
		for (int j = 0; j < x_cnt; ++j) if (B[i][j] != INT_MAX)
		{
			edge[i + 1].emplace_back(j + b_cnt + 1);
			edge[j + b_cnt + 1].emplace_back(i + 1);
			cost[i + 1][j + b_cnt + 1] = B[i][j];
			cost[j + b_cnt + 1][i + 1] = 0;
			capacity[i + 1][j + b_cnt + 1] = 1;
			capacity[j + b_cnt + 1][i + 1] = 0;
		}
	}
	for (int j = b_cnt + 1; j <= b_cnt + x_cnt; ++j)
	{
		edge[j].emplace_back(_t);
		edge[_t].emplace_back(j);
		cost[j][_t] = 0;
		cost[_t][j] = 0;
		capacity[j][_t] = 1;
		capacity[_t][j] = 0;
	}
}
bool bellman()
{
	d.assign(200, INT_MAX);
	d[_s] = 0;
	queue<int> Q;
	vector<bool> inQ(200, false);
	Q.emplace(_s);
	inQ[_s] = true;
	while (!Q.empty())
	{
		int u = Q.front();
		Q.pop();
		inQ[u] = false;
		for (auto& v : edge[u])
		{
			if (net[v][u] > 0 && d[u] + (-cost[v][u]) < d[v])
				d[v] = d[u] + (-cost[v][u]);
			else if (capacity[u][v] > net[u][v] && d[u] + cost[u][v] < d[v])
				d[v] = d[u] + cost[u][v];
			else continue;
			p[v] = u;
			if (!inQ[v]) Q.emplace(v), inQ[v] = true;
		}
	}
	if (d[_t] == INT_MAX) return false;
	else return true;
}
void updateflow(int u, int v, int c)
{
	if (v == _s) return;
	net[u][v] += c;
	net[v][u] -= c;
	updateflow(p[u], u, c);
}
int maxflow()
{
	int ret = 0;
	while (bellman())
	{
		ret += d[_t];
		updateflow(p[_t], _t, 1);
	}
	return ret;
}
int main()
{
	cin >> T;
	while (T--)
	{
		init();
		read();
		cout << maxflow() << "\n";
	}
}

# 參考資料

https://www.larrysprognotes.com/UVa-10888/