传送门
分配问题就是将人和工作分开来做匹配,但是与一般的二分图匹配不同的是,每个匹配都是有权值的。这个问题就是二分图最大权完美匹配问题。
虽然这个问题可以用二分图最大权完美匹配的专门算法KM算法解决,但是这里只讲网络流解法。
首先发现人和工作的下标都是1~n的,直接建图会产生歧义,于是我们可以定义人的下标是1~n,而工作的下标是n + 1~2n。
对于第 i i 个人去做第 j j 个工作会产生的效益为 Ci,j C i , j ( i,j≤n i , j ≤ n ),相当于从 i i 向 n+j n + j 连一条容量为1,费用为c的一条边,最后(也可以在一开始),建立源点 s s 和汇点 t t ,从 s s 向下标为1 ~ n的所有结点都连一条容量为1,费用为0的边,类似地,我们从下标为n + 1 ~ 2n的所有结点都向 t t 连一条容量为1,费用为0的边接着从 s s 到 t t 跑一遍最小费用最大流即可,所求得的最小费用就是第一问的答案,对于第二问求最大收益该怎么解决呢?其实也不难想,重构一遍图,将容量设为原来容量的相反数,再跑一遍最小费用最大流,将所求的的最小费用取个相反数就是第二问的答案。
#include
#include
#include
#include
#include
#include
#define min(x, y) ((x) < (y) ? (x) : (y))
int a[101][101];
struct Graph {
static const int infty = 0x7f7f7f7f;
struct edgetype {
int to, next, flow, cap, cost;
};
int n, m, s, t;
std::vector< edgetype > edge;
std::vector< bool > inQ;
std::vector< int > head, dist, path;
std::queue< int > Q;
Graph() {
n = m = 0;
head.clear(); dist.clear(); edge.clear(); inQ.clear(); path.clear();
}
Graph(int n, int m) {
this->n = n; this->m = m;
head.resize(n + 1); dist.resize(n + 1); inQ.resize(n + 1); path.resize(n + 1);
head.assign(n + 1, -1);
}
void AddEdge(int from, int to, int cap, int cost) {
edge.push_back((edgetype){to, head[from], 0, cap, cost});
head[from] = edge.size() - 1;
edge.push_back((edgetype){from, head[to], 0, 0, -cost});
head[to] = edge.size() - 1;
}
bool FindPath() {
dist.assign(n + 1, infty);
inQ.assign(n + 1, false);
path.assign(n + 1, -1);
while (!Q.empty()) Q.pop();
Q.push(s); dist[s] = 0; inQ[s] = true;
while (!Q.empty()) {
int u = Q.front(); Q.pop(); inQ[u] = false;
for (int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].to;
if (edge[i].flow < edge[i].cap && dist[v] > dist[u] + edge[i].cost) {
dist[v] = dist[u] + edge[i].cost; path[v] = i;
if (!inQ[v]) {
Q.push(v);
inQ[v] = true;
}
}
}
}
return path[t] != -1;
}
void Argument(long long& Mincost) {
long long alpha = infty;
for (int i = path[t]; i != -1; i = path[edge[i ^ 1].to])
alpha = min(alpha, edge[i].cap - edge[i].flow);
Mincost += dist[t] * alpha;
for (int i = path[t]; i != -1; i = path[edge[i ^ 1].to]) {
edge[i].flow += alpha;
edge[i ^ 1].flow -= alpha;
}
}
long long MincostMaxflow(int s, int t) {
this->s = s; this->t = t;
long long Mincost = 0;
while (FindPath())
Argument(Mincost);
return Mincost;
}
};
Graph G;
int main() {
int n;
scanf("%d", &n);
G = Graph(2 * n + 2, n * n + 2 * n);
for (int i = 1; i <= n; i++) {
G.AddEdge(0, i, 1, 0);
G.AddEdge(n + i, n * 2 + 1, 1, 0);
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
scanf("%d", &a[i][j]);
G.AddEdge(i, n + j, 1, a[i][j]);
}
printf("%lld\n", G.MincostMaxflow(0, 2 * n + 1));
G = Graph(2 * n + 2, n * n + 2 * n);;
for (int i = 1; i <= n; i++) {
G.AddEdge(0, i, 1, 0);
G.AddEdge(n + i, n * 2 + 1, 1, 0);
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
G.AddEdge(i, n + j, 1, -a[i][j]);
printf("%lld\n", -G.MincostMaxflow(0, 2 * n + 1));
return 0;
}