[费用流][动态开边] Jzoj P4371 作业分配
题解
- 一眼,有点费用流的感觉
- 建图很简单,每科向S源点连一条费用为0流量为a[i]的边
- 每人向每科连一条费用为c[i][j]流量为a[j]的边
- 最后每人向汇点T连一条费用为0流量为b[i]的边
- 然后跑一遍费用流,这样就有40分了
- 这题时限是500ms!!!
- 所以,一开始将所有边加入图里会超时
- 那么考虑一下动态加边
- 就可以100分了
- 动态加边的做法:
- 动态加边就是在之前加入的最短边满流后才加的边
- 也就是说我们一开始只加入与第i科最短的边(也就是做第i科需要最短时间的人)(快排排个序)
- 如果在跑的过程中满流了,那么对于当前来说,费用绝对是最优的
- 因为,我们是从最短加到最长,知道搞定为止
- 所以当前的费用绝对是最优的
- 然后我们就依次往后加边就好了
代码
1 #include<cstdio> 2 #include<iostream> 3 #include<queue> 4 #include<cstring> 5 #include<algorithm> 6 using namespace std; 7 const int N=10005; 8 const int inf=100000000; 9 int n,m,cnt=1,last[N],t,f[N],d[N],s,a[510],b[510],k[N],num[N],ans,dis[N],w[N],mx; 10 struct node{int d,v;}c[510][510]; 11 struct edge{int to,next,c,w;}e[N*100]; 12 queue<int> Q; 13 void insert(int u,int v,int x,int y) 14 { 15 e[++cnt].to=v; e[cnt].c=x; e[cnt].w=y; e[cnt].next=last[u]; last[u]=cnt; 16 e[++cnt].to=u; e[cnt].c=0; e[cnt].w=-y; e[cnt].next=last[v]; last[v]=cnt; 17 } 18 bool spfa() 19 { 20 for (int i=s;i<=t;i++) 21 { 22 dis[i]=inf; 23 f[i]=d[i]=0; 24 } 25 Q.push(s); 26 f[s]=1; dis[s]=0; w[s]=inf; 27 while (!Q.empty()) 28 { 29 int u=Q.front(); Q.pop(); 30 for (int i=last[u];i;i=e[i].next) 31 if (e[i].c&&e[i].w+dis[u]<dis[e[i].to]) 32 { 33 dis[e[i].to]=e[i].w+dis[u]; 34 w[e[i].to]=min(w[u],e[i].c); 35 d[e[i].to]=i; 36 if (!f[e[i].to]) 37 { 38 f[e[i].to]=1; 39 Q.push(e[i].to); 40 } 41 } 42 f[u]=0; 43 } 44 if (dis[t]<inf) return 1; else return 0; 45 } 46 void mcf() 47 { 48 int x=t; 49 while (x!=s) 50 { 51 int i=d[x]; 52 e[i].c-=w[t]; 53 e[i^1].c+=w[t]; 54 x=e[i^1].to; 55 } 56 mx+=w[t]; 57 ans+=dis[t]*w[t]; 58 } 59 void work() 60 { 61 while (spfa()) 62 { 63 mcf(); 64 for (int i=1;i<=n;i++) 65 while (!e[k[c[i][num[i]].d]].c&&num[i]<m) num[i]++,insert(i,n+c[i][num[i]].d,a[i],c[i][num[i]].v); 66 } 67 } 68 bool cmp(node x,node y) { return x.v<y.v; } 69 int main() 70 { 71 scanf("%d%d",&n,&m); 72 s=0; t=m+n+1; 73 for (int i=1;i<=n;i++) scanf("%d",&a[i]); 74 for (int i=1;i<=m;i++) scanf("%d",&b[i]); 75 for (int i=1;i<=n;i++) 76 { 77 78 for (int j=1;j<=m;j++) scanf("%d",&c[i][j].v),c[i][j].d=j; 79 sort(c[i]+1,c[i]+m+1,cmp); 80 num[i]=1; 81 insert(i,n+c[i][1].d,a[i],c[i][1].v); 82 } 83 for (int i=1;i<=n;i++) insert(s,i,a[i],0); 84 for (int i=1;i<=m;i++) insert(n+i,t,b[i],0),k[i]=cnt-1; 85 work(); 86 printf("%d",ans); 87 return 0; 88 }