http://poj.org/problem?id=3686
题意:有n个玩具和m台机器,给出每个玩具在每台机器上加工完成的时间,并且每台机器同一时间只能加工一个玩具,求加工完所有的玩具所需要的最少平均时间。
思路:
将n个玩具看做一个集合,m台机器看做一个集合,将每个玩具在每台机器上加工时间看做边,很容易想到二分图最佳匹配问题。
设N个任务的执行时间分别为T1,T2…TN,则N个订单的总的执行时间是
T1*N + T2*(N-1) + … + TN-1*2 + TN。
本题的关键是拆点构图。将每台机器拆成n个机器分点,将玩具i与机器j的第k个分点相连,容量为1,权值是map[i][j]*k,表示玩具i是机器j上倒数第k个完成的,完成该任务所消耗的时间是map[i][j]*k;注意是消耗的时间不是生产的时间,消耗的时间包括生产时间以及对后面k-1个玩具的延误时间。再附加一个超级源点与每个玩具相连,容量为1,权值为0;每个机器拆点后与超级汇点相连,容量为1,权值为0。构图完成。然后在其基础上求最小费用最大流。
这样构图后,总的顶点数为 n+n*m+2,总的边数是 (n+n*n*m+n*m)*2;乘2是因为加边是要加上反向边,容量为0,权值为其负值。
#include <stdio.h> #include <string.h> #include <algorithm> #include <queue> using namespace std; const int maxn = 2600; const int INF = 0x3f3f3f3f; int n,m,s,t,vn; int map[55][55]; struct node { int u,v,w,c,next; }edge[800000]; int p[maxn],cnt; int pre[maxn],dis[maxn]; void add(int u, int v, int w, int c) { edge[cnt].u = u; edge[cnt].v = v; edge[cnt].w = w; edge[cnt].c = c; edge[cnt].next = p[u]; p[u] = cnt++; edge[cnt].u = v; edge[cnt].v = u; edge[cnt].w = 0; edge[cnt].c = -c; edge[cnt].next = p[v]; p[v] = cnt++; } void init() //拆点构图 { scanf("%d %d",&n,&m); for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) scanf("%d",&map[i][j]); cnt = 0; memset(p,-1,sizeof(p)); s = 0; //超级源点 t = n+n*m+1;//超级汇点 vn = t+1; //总顶点数 for(int i = 1; i <= n; i++) add(s,i,1,0); for(int i = n+1; i <= n+n*m; i++) add(i,t,1,0); for(int i = 1; i <= n; i++) { int count = n+1; for(int j = 1; j <= m; j++) { for(int k = 1; k <= n; k++) add(i,count++,1,map[i][j]*k); } } } bool spfa() //spfa寻找增广路 { queue<int>que; while(!que.empty()) que.pop(); int inque[maxn]; memset(pre,-1,sizeof(pre)); memset(inque,0,sizeof(inque)); for(int i = 0; i <= vn; i++) dis[i] = INF; dis[s] = 0; inque[s] = 1; que.push(s); while(!que.empty()) { int u = que.front(); que.pop(); inque[u] = 0; for(int i = p[u]; i != -1; i = edge[i].next) { int v = edge[i].v; int c = edge[i].c; if(edge[i].w && dis[v] > dis[u]+c) { dis[v] = dis[u]+c; pre[v] = i; if(!inque[v]) { inque[v] = 1; que.push(v); } } } } if(dis[t] < INF) return true; else return false; } void solve() { int ans = 0; while(spfa()) { for(int i = pre[t]; i != -1; i = pre[edge[i].u]) { edge[i].w--; edge[i^1].w++; } ans += dis[t]; //更新总费用 } double res = (ans*1.0)/n; printf("%.6f\n",res); } int main() { int test; scanf("%d",&test); while(test--) { init(); solve(); } return 0; }