poj - 3686 The Windy's (KM算法)
题意:n个订单和m个生产车间,每个订单在不同的车间生产所需要的时间不一样,并且每个订单只能在同一个车间中完成,直到这个车间完成这个订单就可以生产下一个订单.现在需要求完成n个订单的平均时间最少是多少.(每个订单的单独时间之和/n,包括等待时间)。
主要是建图,考虑第i个订单在第j个车间倒数第k个被生产,那么第i个订单在第j个区间所花费的时间为k*mat[i][j].
每个区间最多生产n个订单,那么就可以把n*m的图转化成n*(n*m)的图进而用km算法求最小权值。
所以把每个权值取反进而求最大权匹配,但是为什么不能直接求最小权匹配还是没想清楚.
注意G++用%f输出,否则wa.
参考:http://www.tuicool.com/articles/jmYNryf
1 #include<iostream> 2 #define max(a,b) ((a)>(b)?(a):(b)) 3 #define min(a,b) ((a)<(b)?(a):(b)) 4 using namespace std; 5 const int nMax = 52; 6 const int mMax = 2502; 7 const int inf = 99999999; 8 9 int n, m; // n为X集合顶点数量,m为Y集合顶点数量。(1~n和1~m) 10 int map[nMax][mMax]; // map[i][j]:记录X集合中的i到Y集合中的j所需的费用。 11 int lx[nMax], ly[mMax]; 12 int link[mMax]; 13 bool x[nMax], y[mMax]; 14 15 bool dfs(int u){ // 判断能否找到最大完全匹配。 16 x[u] = true; 17 for(int v = 1; v <= m; v ++) 18 if(!y[v] && lx[u] + ly[v] == map[u][v]){ 19 y[v] = true; 20 if(!link[v] || dfs(link[v])){ 21 link[v] = u; 22 return true; 23 } 24 } 25 return false; 26 } 27 28 int KM(){ // KM算法。 29 int i, j, k, mi; 30 for(i = 1; i <= n; i ++) 31 for(lx[i] = -inf, j = 1; j <= m; j ++) 32 lx[i] = max(lx[i], map[i][j]); 33 memset(ly, 0, sizeof(ly)); 34 memset(link, 0, sizeof(link)); 35 for(k = 1; k <= n; k ++){ 36 while(1){ 37 memset(x, 0, sizeof(x)); 38 memset(y, 0, sizeof(y)); 39 if(dfs(k)) break; 40 mi = inf; 41 for(i = 1; i <= n; i ++) 42 if(x[i]) 43 for(j = 1; j <= m; j ++) 44 if(!y[j]) 45 mi = min(mi, lx[i] + ly[j] - map[i][j]); 46 for(i = 1; i <= n; i ++) if(x[i]) lx[i] -= mi; 47 for(i = 1; i <= m; i ++) if(y[i]) ly[i] += mi; 48 } 49 } 50 int ans = 0; 51 for(i = 1; i <= m; i ++) 52 if(link[i] > 0) 53 ans += map[link[i]][i]; 54 return ans; 55 } 56 57 int main(){ 58 int t, N, M, i, j, k, mat[nMax][nMax]; 59 scanf("%d", &t); 60 while(t --){ 61 scanf("%d%d", &N, &M); 62 for(i = 1; i <= N; i ++) 63 for(j = 1; j <= M; j ++) 64 scanf("%d", &mat[i][j]); 65 n = N, m = N * M; 66 for(i = 1; i <= N; i ++) // KM算法是求最大权,故这里存负数,最后取反求最小权。 67 for(j = 1; j <= N; j ++) 68 for(k = 1; k <= M; k ++){ 69 map[i][(k-1)*N+j] = -j*mat[i][k]; 70 //cout << i << ' ' << (k-1)*N+j << ' ' << j*mat[i][k] << endl; 71 } 72 double ans = 1.0*(-KM())/N; // 精度必须取double才AC。 73 printf("%.6f\n", ans); 74 } 75 return 0; 76 }
这题还可以用费用流来解决.不过效率没有KM高.
初始源点和每个订单相连,每个车间拆成n*m个车间,然后和汇点相连.然后和km考虑的一样在订单和车间之间连边.
1 #include<cstdio> 2 #include<queue> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int maxn = 2550; 7 const int INF = 1e9; 8 struct edge{ 9 int to,next; 10 int cap,flow,cost; 11 }e[maxn*maxn]; 12 int n,m; 13 int head[maxn],sz,res; 14 bool inq[maxn]; 15 int p[maxn],d[maxn],a[maxn]; 16 void addedge(int u,int v,int cap,int cost){ 17 e[sz].to = v;e[sz].next = head[u]; 18 e[sz].cap = cap;e[sz].flow = 0;e[sz].cost = cost; 19 head[u] = sz ++; 20 e[sz].to = u;e[sz].next = head[v]; 21 e[sz].cap = 0;e[sz].flow = 0;e[sz].cost = -cost; 22 head[v] = sz ++; 23 } 24 void init(){ 25 memset(head,-1,sizeof(head)); 26 sz = 0; 27 } 28 queue<int> Q; 29 int SPFA(int s,int t){ 30 memset(inq,0,sizeof(inq)); 31 for(int i = 0 ; i <= t ; i ++) d[i] = INF; 32 inq[s] = 1;d[s] = 0;p[s] = -1; 33 a[s] = INF; 34 Q.push(s); 35 while(!Q.empty()){ 36 int u = Q.front();Q.pop(); 37 inq[u] = 0; 38 for(int i = head[u] ; i != -1 ; i = e[i].next){ 39 int v = e[i].to; 40 if(e[i].cap - e[i].flow > 0 && e[i].cost + d[u] < d[v]){ 41 d[v] = d[u] + e[i].cost; 42 a[v] = min(e[i].cap - e[i].flow,a[u]); 43 p[v] = i; 44 if(!inq[v]){ 45 inq[v] = 1; 46 Q.push(v); 47 } 48 } 49 } 50 } 51 int u = t; 52 if(d[t] == INF) return 0; 53 res += d[t] * a[t]; 54 while(u != s){ 55 e[ p[u] ].flow += a[t]; 56 e[ p[u] ^ 1 ].flow -= a[t]; 57 u = e[ p[u] ^ 1 ].to; 58 } 59 return 1; 60 } 61 62 void MCMF(int s,int t){ 63 res = 0; 64 while(SPFA(s,t)); 65 printf("%.6f\n",res*1.0/n); 66 } 67 68 void solve(){ 69 init(); 70 int s,t,mat[55][55]; 71 scanf("%d%d",&n,&m); 72 s = 0;t = n * m + n + 1; 73 for(int i=1;i<=n;i++) 74 for(int j=1;j<=m;j++) 75 scanf("%d",&mat[i][j]); 76 for(int i = 1; i <= n; i ++) 77 { 78 addedge(s,i,1,0); 79 for(int j = 1; j <= n; j ++) 80 { 81 for(int k = 1; k <= m; k ++) 82 { 83 addedge(i,n+(k-1)*n+j,1,j*mat[i][k]); 84 } 85 } 86 } 87 for(int i=n+1;i<=n*m+n;i++) 88 addedge(i,t,1,0); 89 MCMF(s,t); 90 } 91 int main() 92 { 93 //freopen("a.txt","r",stdin); 94 int t; 95 scanf("%d",&t); 96 while(t--) solve(); 97 return 0; 98 }