POJ 3686 *最小费用流-转化成普通指派问题)
题意】
有N个订单和M个机器,给出第i个订单在第j个机器完成的时间Mij,每台机器同一时刻只能处理一个订单,机器必须完整地完成一个订单后才能接着完成下一个订单。问N个订单完成时间的平均值最少为多少。
分析 :
最小费用最大流
如果每个工厂只能完成一个订单的话,那就是指派问题了。跑一遍最小费用流即可。但是题目每个工厂可能完成多个。
所以需要将其拆点使得每个工厂只能完成一个订单,进而转换成指派问题。对一个工厂来说,如果每个订单都在这个
工厂完成的话,那么时间为T=t1 + (t1+t2) + (t1+t2+t3) +... = n*t1 + (n-1)*t2 + (n-2)*t3 + ...就可将其看成
是n个只能完成一个订单的工厂只不过它们需要乘于花费的1到N倍。
#include <iostream> #include <queue> #include <vector> #include <cstdio> #include <cstring> using namespace std; const int maxn = 3000; const int INF = 0x3f3f3f3f; typedef pair<int,int> P; struct Edge { int to, cap, cost, rev; Edge(int to_, int cap_, int cost_, int rev_):to(to_),cap(cap_),cost(cost_),rev(rev_){} }; vector<Edge> G[maxn]; int V, n, m, relation[55][55]; int h[maxn], dist[maxn], prevv[maxn], preve[maxn]; void add_edge(int from, int to, int cap, int cost) { G[from].push_back(Edge(to, cap, cost, G[to].size())); G[to].push_back(Edge(from, 0, -cost, G[from].size()-1)); } int min_cost_flow(int s, int t, int f) { int res = 0; memset(h, 0, sizeof(h)); while(f > 0) { priority_queue<P, vector<P>, greater<P> > pq; fill(dist, dist + V, INF); dist[s] = 0; pq.push(P(0, s)); while(!pq.empty()) { P p = pq.top(); pq.pop(); int v = p.second; if(dist[v] < p.first) continue; for(int i = 0; i < G[v].size(); i++) { Edge& e = G[v][i]; if(e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]) { dist[e.to] = dist[v] + e.cost + h[v] - h[e.to]; prevv[e.to] = v; preve[e.to] = i; pq.push(P(dist[e.to], e.to)); } } } if(dist[t] == INF) return -1; for(int v = 0; v < V; v++) h[v] += dist[v]; int d = f; for(int v = t; v != s; v = prevv[v]) { d = min(d, G[prevv[v]][preve[v]].cap); } f -= d; res += d * h[t]; for(int v = t; v != s; v = prevv[v]) { Edge& e = G[prevv[v]][preve[v]]; e.cap -= d; G[v][e.rev].cap += d; } } return res; } int main() { //freopen("in.txt", "r", stdin); int T; scanf("%d", &T); getchar(); while(T--) { scanf("%d%d", &n, &m); for(int i = 0; i < maxn; i++) G[i].clear(); for(int i = 0; i < n; i++) { for(int j = 0; j < m; j++) { scanf("%d", &relation[i][j]); } } int s = n + n * m, t = s + 1; for(int j = 0; j < m; j++) {//工厂 for(int k = 0; k < n; k++) {//将工厂拆成n个点 add_edge(n + j * n + k, t, 1, 0); for(int i = 0; i < n; i++) { add_edge(i, n + j * n + k, 1, (k + 1) * relation[i][j]); } } } for(int i = 0; i < n; i++) add_edge(s, i, 1, 0); V = t + 1; printf("%.6f\n", (double)min_cost_flow(s, t, n) / n); } return 0; }