标签:
一个nxm的地图,地图上的横纵交错成nxm个交叉点,其中有k个交叉点为房间,k个交叉点为k个小人的初始位置。小人可以在地图上沿着水平或垂直方向行走,每走一步的代价为1。求这k个小人分别到达k个不同的房间,所花费的总代价的最小值。
k个小人走到k个房间节点,走出k条不同的路径,形成一个网络,求出花费最少的k条路径。每个房间只能容纳一个小人,视为小人节点到房间节点的路径上的容量为1,这样就不会出现多个小人挤到同一个房间。那么可以将问题转化为网络流:
添加源点s和汇点t,从s出发引出k条边分别到达k个小人,边的容量为1,费用为0;从k个房间节点分别引出一条边到达t,边的容量为1,费用为0;从k个小人节点分别引出k条边达到k个房间节点,边的容量为1,单位费用为小人到房间的最短距离。这样就构造出了一个网络流图,然后求解从源点s到达汇点t的最小费用最大流。
ps:这种有限制条件(比如容量有限制)的问题可以考虑转化为网络流。
#include<stdio.h> #include<string.h> #include<queue> #include<vector> #include<algorithm> #include<cmath> using namespace std; #define INF 1 << 25 #define MAX_NODE 220 #define MAX_EDGE_NUM 40005 #define min(a, b) a<b?a:b struct Edge{ int to; int vol; int cost; int next; }; Edge gEdges[MAX_EDGE_NUM]; int gPre[MAX_NODE]; int gPath[MAX_NODE]; int gDist[MAX_NODE]; int gHead[MAX_NODE]; int gEdgeCount; void InsertEdge(int u, int v, int vol, int cost){ gEdges[gEdgeCount].to = v; gEdges[gEdgeCount].vol = vol; gEdges[gEdgeCount].cost = cost; gEdges[gEdgeCount].next = gHead[u]; gHead[u] = gEdgeCount++; gEdges[gEdgeCount].to = u; gEdges[gEdgeCount].vol = 0; gEdges[gEdgeCount].cost = -cost; gEdges[gEdgeCount].next = gHead[v]; gHead[v] = gEdgeCount++; } //spfa算法求最短路(即增广单位费用最小的从源到汇的路径) bool Spfa(int s, int t){ memset(gDist, 0x7F, sizeof(gDist)); memset(gPre, -1, sizeof(gPre)); gDist[s] = 0; queue<int>Q; Q.push(s); while (!Q.empty()){ int u = Q.front(); Q.pop(); for (int e = gHead[u]; e != -1; e = gEdges[e].next){ int v = gEdges[e].to; if (gEdges[e].vol > 0 && gDist[v] > gDist[u] + gEdges[e].cost){ gDist[v] = gDist[u] + gEdges[e].cost; gPre[v] = u; gPath[v] = e; Q.push(v); } } } return gPre[t] != -1; } int MinCostFlow(int s, int t){ int cost = 0; int max_flow = 0; int u, v, e; while (Spfa(s, t)){ int f = INF; for (u = t; u != s; u = gPre[u]){ f = min(f, gEdges[gPath[u]].vol); } for (u = t; u != s; u = gPre[u]){ e = gPath[u]; gEdges[e].vol -= f; gEdges[e^1].vol += f; //反向边 } max_flow += f; cost += f*gDist[t]; } return cost; } char gMap[105][105]; vector<pair<int, int> > gHousVec; vector<pair<int, int> > gManVec; //建图 void BuildGraph(){ int n = gHousVec.size(); // 源点0,连接到每个小人结点,流量为1,单位费用为0 for (int u = 1; u <= n; u++){ InsertEdge(0, u, 1, 0); } for (int i = 0; i < n; i++){ for (int j = 0; j < n; j++){ //求出小人i到房间j的最短距离 int min_dist = abs(gManVec[i].first - gHousVec[j].first) + abs(gManVec[i].second - gHousVec[j].second); //建边,连接小人i和房间j InsertEdge(i + 1, j + n + 1, 1, min_dist); } } //汇点 2*n+1,连接各个房间借点到汇点,流量为1,单位费用为0 for (int u = 1; u <= n; u++){ InsertEdge(n + u, 2 * n + 1, 1, 0); } } int main(){ int n, m; while (scanf("%d %d", &n, &m) && n && m){ char tmp; gHousVec.clear(); //初始化 gManVec.clear(); memset(gHead, -1, sizeof(gHead)); //图的初始化 gEdgeCount = 0; //图的初始化 for (int i = 1; i <= n; i++){ getchar(); for (int j = 1; j <= m; j++){ scanf("%c", &tmp); if (tmp == ‘H‘){ gHousVec.push_back(pair<int, int>(i, j)); } else if (tmp == ‘m‘){ gManVec.push_back(pair<int, int>(i, j)); } } } BuildGraph(); //建图 int cost = MinCostFlow(0, 2 * gHousVec.size() + 1); //求解最小费用最大流 printf("%d\n", cost); } return 0; }
标签:
原文地址:http://www.cnblogs.com/gtarcoder/p/4890944.html