题目大意:
有n个订单m个车间,每个车间均可以单独完成任何一个订单。每个车间完成不同订单的时间是不同的。不会出现两个车间完成同一个订单的情况。给出每个订单在某个车间完成所用的时间。问订单完成的最小平均时间是多少。
这个题在建图上有一些需要思考很长时间的地方。因为每个订单所消耗的时间是车间完成订单的时间加上订单等待的时间。我们设在车间A需要完成k个订单,消耗的总时间是t1+(t1+t2)+(t1+t2+t3)……转换一下就是t1*k+t2*(k-1)+t3*(k-2)……我们就找到了规律:当第i个订单在第j个车间是倒数第k个任务时,总消耗时间需要加上订单i在车间对应消耗时间的k倍。
将m个车间都拆成n个,形成一个m*n的m行n列矩阵,表示第i个任务在第j个车间的倒数第k个时间段完成,
源点是0,汇点t ,所以, 点的总数是 n +m*n +2 ;
#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N= 5000 ;
const int M= 800000 ;
const int inf =1<< 30 ;
struct node
{
int u ,v,c,cost ,next ;
}edge[M] ;
int head[N],pp[N],pre[N],dist[N],vist[N] ;
int top ;
int cost[100][100];
void add(int u ,int v,int c,int cost)
{
edge[top].u=u;
edge[top].v=v;
edge[top].c=c;
edge[top].cost=cost;
edge[top].next=head[u];
head[u]=top++;
edge[top].u=v;
edge[top].v=u;
edge[top].c=0;
edge[top].cost=-cost;
edge[top].next=head[v];
head[v]=top++;
}
int SPFA(int s,int t)
{
int u , v ;
memset(vist,0,sizeof(vist));
memset(pre,-1,sizeof(pre));
for(int i = 0 ; i <= t ; i++) dist[i]=inf ;
vist[s]=1;dist[s]=0;pre[s]=s;
queue<int>q;
q.push(s);
while(!q.empty())
{
u=q.front();
q.pop();
vist[u]=0;
for(int i =head[u];i!=-1;i=edge[i].next)
{
v=edge[i].v;
if(edge[i].c && dist[v] > dist[u]+edge[i].cost)
{
dist[v] = dist[u]+edge[i].cost ;
pre[v]=u;
pp[v]=i;
if(!vist[v]);
{
vist[v]=1;
q.push(v);
}
}
}
}
if(dist[t]==inf) return 0;
return 1 ;
}
int MFMC(int s,int t)
{
int mincost=0,flow=0,minflow ;
while(SPFA(s,t))
{
minflow=inf;
for(int i=t;i!=s;i=pre[i])
minflow=min(minflow,edge[pp[i]].c);
for(int i=t;i!=s;i=pre[i])
{
edge[pp[i]].c -= minflow;
edge[pp[i]^1].c += minflow;
}
flow += minflow;
mincost += dist[t]*minflow ;
// printf("****");
}
return mincost ;
}
int main()
{
int T ,x,n,m;
scanf("%d",&T) ;
while(T--)
{
top = 0 ;
memset(head,-1,sizeof(head)) ;
memset(cost,0,sizeof(cost)) ;
scanf("%d%d",&n,&m);
for(int i = 1 ; i <= n ; i++)
for(int j = 1 ; j <= m ; j++)
scanf("%d",&cost[i][j]) ;
int s = 0 , t = n+m*n+1 ;
for(int i = 1 ; i <= n ; i++) //i任务
for(int j = 1 ;j <= m ; j++) //j车间
for(int k = 1 ; k <= n ; k++)//倒数第k个时段完成 ;
add(i,n+(j-1)*n+k,1,cost[i][j]*k) ;
for(int i = 1 ; i <= n ; i++)
add(s,i,1,0) ;
for(int i = n+1 ; i <= n+m*n ; i++)
add(i,t,1,0) ;
int ans = MFMC(s,t) ;
printf("%.6lf\n",ans*1.0/n) ;
}
return 0 ;
}