问题描述
文德玩具厂是世界著名的玩具厂,拥有 M个 世界一流的玩具生产车间。今年经理收到了N个玩具订单。经理知道每个订单在不同的车间需要不同的时间。更准确地说,如果玩具是在第j个车间生产的,第i个订单将需要Zij个小时。此外,每个订单的工作必须完全在同一个车间完成。一个车间要完成前一个订单才能转到下一个订单。这种转换不需要任何时间。
经理想要最小化N个订单的平均完成时间。你能帮助他吗?
输入
输入的第一行是测试用例的数量。每个测试用例的第一行包含两个整数,N和M(1≤N,M≤50)。
接下来的N行每个包含M个整数,描述矩阵Zij(1≤Zij≤100,000)每个测试用例前有一个空行。
输出
对于每个测试用例,将答案输出到一行中。结果应该四舍五入到小数点后六位。
Sample Input
3
3 4
100 100 100 1
99 99 99 1
98 98 98 1
3 4
1 100 100 100
99 1 99 99
98 98 1 98
3 4
1 100 100 100
1 99 99 99
98 1 98 98
Sample Output
2.000000
1.000000
1.333333
思路:
如果每个工厂只能完成一个订单,那就是简单指派问题了。跑一遍KM或者MCMF就解决了。但是这个题目中每个工厂可能完成多个。
对一个工厂来说,如果每个订单都在这个工厂完成的话,那么:
第一个完成时间为t
第二个完成时间为t+t
第三个完成时间为t+t+t
…
把每个工厂拆成n个点,时间分别为t,t+t,t+t+t,…
然后就变成指派问题求最优解了,跑KM或MCMF
MCMF就是加个源点汇点,不写了
code(KM):
#include<cstring>
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;
const int maxm=55;
const int inf=1e9;
int z[maxm][maxm];
int g[maxm][maxm*maxm];
int lx[maxm],ly[maxm*maxm];
int visx[maxm],visy[maxm*maxm];
int need[maxm*maxm];
int now[maxm*maxm];
int nx,ny;
int n,m;
bool dfs(int x){
visx[x]=1;
for(int i=1;i<=ny;i++){
if(!visy[i]){
int temp=lx[x]+ly[i]-g[x][i];
if(temp==0){
visy[i]=1;
if(now[i]==-1||dfs(now[i])){
now[i]=x;
return 1;
}
}else{
need[i]=min(need[i],temp);
}
}
}
return 0;
}
void km(){
for(int i=1;i<=ny;i++){
now[i]=-1;
ly[i]=0;
}
for(int i=1;i<=nx;i++){
lx[i]=g[i][1];
for(int j=2;j<=ny;j++){
lx[i]=max(lx[i],g[i][j]);
}
}
for(int i=1;i<=nx;i++){
for(int j=1;j<=ny;j++){
need[j]=inf;
}
while(1){
for(int j=1;j<=nx;j++){
visx[j]=0;
}
for(int j=1;j<=ny;j++){
visy[j]=0;
}
if(dfs(i))break;
int d=inf;
for(int j=1;j<=ny;j++){
if(!visy[j]){
d=min(d,need[j]);
}
}
for(int j=1;j<=nx;j++){
if(visx[j]){
lx[j]-=d;
}
}
for(int j=1;j<=ny;j++){
if(visy[j]){
ly[j]+=d;
}else{
need[j]-=d;
}
}
}
}
int ans=0;
for(int i=1;i<=ny;i++){
if(now[i]!=-1){
ans+=g[now[i]][i];
}
}
ans=-ans;
printf("%.6f\n",ans*1.0/n);
}
int main(){
int T=11;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&z[i][j]);//i to j
}
}
nx=n;
ny=n*m;
for(int i=1;i<=n;i++){//toy
int cnt=0;
for(int j=1;j<=m;j++){//factor
for(int k=1;k<=n;k++){//拆成n个点
cnt++;
g[i][cnt]=-k*z[i][j];
}
}
}
km();
}
return 0;
}