·· / ·– ·· ·-·· ·-·· / ·–· · ·-· ··· ·· ··· - / ··- -· - ·· ·-·· / ·· / ·– ·· -·
题目来源:http://www.lydsy.com/JudgeOnline/problem.php?id=1070
题目有一点要注意的就是等待时间包括之前的人修车的时间。
应该是比较容易看出费用流的,修车时间就是费用,而每个“修车的人”就被抽象成了流。
本来是写的增广一次就累加那个修车师傅的总时间的,但因为实际增广是可能会“收回”流量,所以并不能输出正解。
这里引用一下http://travisbraps.top/Newt/442
费用流,把每个技术人员拆点,表示其修倒数第i辆车,这样后面的人等的时间就可以简单得出了 从原点连到每个拆烂了的工作人员(orz),费用为0,流量为1 从每辆车连到汇点,费用为0,流量为1 对于对应的拆烂的工作人员和车连边,流量为1,费用为t[k][i]*j //倒数第j辆
这里有两个费用流中非常有意思的思想。
1、第一个是把选择的情况都化成独立的边。这样就可以在增广时“收回”流而不影响正确答案的得出。因为费用流优先走cost小的边,在选cost大的边之前一定已经走了小边。
2、第二个是倒着想。设每个人的修车时间为a[i],等待时间为s[i],则s[n]=a[1]+a[2]+…+a[n],每一个师傅那里总待的时间=s[1]+s[2]+s[3]+…+s[n]。但是如果不考虑这个人前面有多少人修车了(实际也没必要考虑),而只考虑这个人自己修车对后面人的影响,则计算会容易得多。直接考虑这个人是倒数第几个修车的,那么这个人只会影响到他后面的修车的时间,这个人对全局的影响就是t[k][i]*j
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
const int N=1010,M=100020,inf=0x3fffffff;
using namespace std;
int ver[M<<1],nxt[M<<1],e[M<<1],w[M<<1],c[M<<1],head[N],tot=1;
int dis[N],pos[N],pre[N],a[N][N],map[N][N];
bool vis[N];
int m,n,s,t,cost=0,cnt=0;
double ans=0;
void add (int u,int v,int w,int cost) {
ver[++tot]=v;e[tot]=w;c[tot]=cost;nxt[tot]=head[u];head[u]=tot;
ver[++tot]=u;e[tot]=0;c[tot]=-cost;nxt[tot]=head[v];head[v]=tot;
}
bool spfa () {
memset(pre,-1,sizeof(pre));
for (int i=0;i<N;i++) dis[i]=inf;
memset(pos,0,sizeof(pos));
memset(vis,0,sizeof(vis));
queue<int> q; int now,v;
q.push(s); vis[s]=1; dis[s]=0; pre[s]=s;//
while (!q.empty()) {
now=q.front(); q.pop();
for (int i=head[now];i;i=nxt[i]) {
v=ver[i];
if (e[i]&&dis[v]>dis[now]+c[i]) {
dis[v]=dis[now]+c[i];
pos[v]=i; pre[v]=now;
if (!vis[v]) q.push(v),vis[v]=1;//
}
}
vis[now]=0;
}
return pre[t]!=-1;//
}
void mcf () {
int aug;
while (spfa()) {
aug=inf;
for (int i=t;i!=s;i=pre[i]) aug=min(aug,e[pos[i]]);
cost+=aug*dis[t];
for (int i=t;i!=s;i=pre[i])
e[pos[i]]-=aug,e[pos[i]^1]+=aug;
}
}
int main () {
scanf("%d%d",&m,&n);
s=0,t=n+m*n+1;
for (int i=1;i<=m;i++)
for (int j=1;j<=n;j++)
map[i][j]=++cnt;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
for (int i=1;i<=n;i++)
add(s,i,1,0);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
for (int k=1;k<=n;k++)
add(i,n+map[j][k],1,a[i][j]*k);
for (int i=1;i<=m;i++)
for (int j=1;j<=n;j++)
add(n+map[i][j],t,1,0);
mcf();
ans=cost;
printf("%.2lf",ans/n);
}