题目链接:bzoj 1070
尼玛,这道题我开始一直跪着,换了zkw费用流写之后,居然wa了!!!然后心力交瘁的debug发现,我数组开小了= =||。
又是神一样的建图。首先由于存在先后顺序,当前在这个技术人员修车的顾客所花的时间会影响下一个在该技术人员修车的顾客。因此,我们暴力拆点,将每个技术人员拆成N个,每个顾客若是倒数第k个在该技术人员这修车,就将该顾客与该技术人员连一条费用为w*k的边。加入源点和汇点,跑zkw费用流即可。
zkw中所需要的双向队列,我是手写的,应为STL的deque确实不熟啊(请神犇自动无视)。。。
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define T (601)
#define inf (1e8)
int N,M,S,ans=0;
int a[100][100];//就是这个数组因为读题读错了开小了,但是评测居然报WA而不是RE这是怎么回事???
head[605],vis[605],q[605],dep[605];
struct node{
int u,v,w,c,next;
}e[100001];
int k=0;
void adde(int u,int v,int w,int c){
e[k].u=u; e[k].v=v; e[k].c=c; e[k].w=w; e[k].next=head[u]; head[u]=k++;
e[k].u=v; e[k].v=u; e[k].c=0; e[k].w=-w; e[k].next=head[v]; head[v]=k++;
}
bool spfa(){
memset(vis,0,sizeof(vis));
for(int i=0;i<=T;i++)dep[i]=inf;
int l=0,r=1;
q[0]=T; dep[T]=0; vis[T]=1;
while(l!=r){
int u=q[l]; l++; if(l==T)l=0;
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].v;
if(e[i^1].c&&dep[v]>dep[u]-e[i].w){
dep[v]=dep[u]-e[i].w;
if(vis[v]==0){
vis[v]=1; q[r++]=v;
if(r==T)r=0;
}
}
}
vis[u]=0;
}
return dep[S]!=inf;
}
int dfs(int x,int k){
vis[x]=1;
if(x==T)return k;
int ret=0;
for(int i=head[x];i!=-1;i=e[i].next){
int v=e[i].v;
if(!vis[v]&&e[i].c&&dep[v]+e[i].w==dep[x]){
int temp=dfs(v,min(k-ret,e[i].c));
e[i].c-=temp; e[i^1].c+=temp;
ret+=temp;
ans+=temp*e[i].w;
if(ret==k)return k;
}
}
return ret;
}
void zkw(){
while(spfa()){
vis[T]=1;
while(vis[T]){
memset(vis,0,sizeof(vis));
dfs(S,inf);
}
}
printf("%.2lf",(double)ans/N);
}
void input(){
scanf("%d%d",&M,&N);
for(int i=1;i<=N;i++)
for(int j=1;j<=M;j++)
scanf("%d",&a[i][j]);
}
void solve(){
memset(head,-1,sizeof(head));
S=0;
for(int i=1;i<=N*M;i++)
adde(0,i,0,1);
for(int i=N*M+1;i<=N*M+N;i++)
adde(i,T,0,1);
for(int i=1;i<=M;i++)
for(int j=1;j<=N;j++)
for(int k=1;k<=N;k++)
adde((i-1)*N+j,M*N+k,a[k][i]*j,1);
zkw();
}
int main(){
input();
solve();
return 0;
}