这道题的思路真是神,万万没想到,给出题人跪了orzorz
我们首先要想明白的是一个人修车对答案的影响是什么,假设一个人是一个技术人员倒数第一个修车,那么它对答案的影响就是修车时间,如果是倒数第二个那么对答案的影响就是2*修车时间,以此类推,所以我们将一个技术人员拆分成m个,代表一个技术人员倒数第几次修车,让这些点分别向所有车连一条容量为一,费用为t[i][j]*k的流,从起点向每一个拆分出来的点连一条容量为1,费用为0的流,每辆车向汇点也连一条这样的流,然后跑一边最小费用流就好了
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<algorithm>
#include<cmath>
using namespace std;
int dis[100000];
struct my_road
{
int r,f,v;
}a[100000];
int fir[100000];
int nex[100000];
int dui[1000000];
bool pd[100000];
int fro[100000];
int tot=1;
int s,t;
void add_edge(int l,int r,int f,int v)
{
a[++tot].r=r;
a[tot].f=f;
nex[tot]=fir[l];
fir[l]=tot;
a[tot].v=v;
}
int cost=0;
int add_flow()
{
int u=t;
int flow=2147483647;
while(u!=s)
{
flow=min(flow,a[fro[u]].f);
u=a[fro[u]^1].r;
}
u=t;
while(u!=s)
{
cost+=a[fro[u]].v*flow;
a[fro[u]].f-=flow;
a[fro[u]^1].f+=flow;
u=a[fro[u]^1].r;
}
return flow;
}
bool spfa()
{
int top=1,my_final=2;
memset(dis,0x1f,sizeof(dis));
memset(pd,0,sizeof(pd));
dui[1]=s;
dis[s]=0;
pd[s]=true;
while(top<my_final)
{
int u=dui[top];
for(int o=fir[u];o!=0;o=nex[o])
{
if(a[o].f && dis[a[o].r]>dis[u]+a[o].v)
{
fro[a[o].r]=o;
dis[a[o].r]=dis[u]+a[o].v;
if(!pd[a[o].r]) dui[my_final++]=a[o].r,pd[a[o].r]=true;
}
}
pd[u]=false;
top++;
}
if(dis[t]==0x1f1f1f1f) return false;
return true;
}
int n,m;
int mid[100][100];
int main()
{
s=0,t=99999;
scanf("%d%d",&m,&n);
for(int i=1;i<=n;i++)
{
add_edge(s,i,1,0);
add_edge(i,s,0,0);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&mid[j][i]);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=n;k++)
{
add_edge(i,n+k*m+j,1,mid[j][i]*k);
add_edge(n+k*m+j,i,0,-mid[j][i]*k);
}
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
add_edge(n+j*m+i,t,1,0);
add_edge(t,n+j*m+i,0,0);
}
int ans=0;
while(spfa())
{
ans+=add_flow();
}
double trueans=cost;
trueans=trueans/double(n);
printf("%.2f",trueans);
return 0;
}