bzoj2879 [Noi2012] 美食节 (费用流)
2879: [Noi2012]美食节
Time Limit: 10 Sec Memory Limit: 512 MB
Submit: 2231 Solved: 1177
[Submit][Status][Discuss]
Description
CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节。作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴。他很快就尝遍了美食节所有的美食。然而,尝鲜的欲望是难以满足的。尽管所有的菜品都很可口,厨师做菜的速度也很快,小M仍然觉得自己桌上没有已经摆在别人餐桌上的美食是一件无法忍受的事情。于是小M开始研究起了做菜顺序的问题,即安排一个做菜的顺序使得同学们的等待时间最短。小M发现,美食节共有n种不同的菜品。每次点餐,每个同学可以选择其中的一个菜品。总共有m个厨师来制作这些菜品。当所有的同学点餐结束后,菜品的制作任务就会分配给每个厨师。然后每个厨师就会同时开始做菜。厨师们会按照要求的顺序进行制作,并且每次只能制作一人份。此外,小M还发现了另一件有意思的事情:
虽然这m个厨师都会制作全部的n种菜品,但对于同一菜品,不同厨师的制作时间未必相同。他将菜品用1, 2, ..., n依次编号,厨师用1, 2,
..., m依次编号,将第j个厨师制作第i种菜品的时间记为 ti,j
。小M认为:每个同学的等待时间为所有厨师开始做菜起,到自己那份菜品完成为止的时间总长度。换句话说,如果一个同学点的菜是某个厨师做的第k道菜,则他的等待时间就是这个厨师制作前k道菜的时间之和。而总等待时间为所有同学的等待时间之和。现在,小M找到了所有同学的点菜信息:
有 pi 个同学点了第i种菜品(i=1, 2, ..., n)。他想知道的是最小的总等待时间是多少。
Input
输入文件的第1行包含两个正整数n和m,表示菜品的种数和厨师的数量。
第2行包含n个正整数,其中第i个数为pi,表示点第i种菜品的人数。
接下来有n行,每行包含m个非负整数,这n行中的第i行的第j个数为ti,j,表示第j个厨师制作第i种菜品所需的时间。
输入文件中每行相邻的两个数之间均由一个空格隔开,行末均没有多余空格。
Output
输出仅一行包含一个整数,为总等待时间的最小值。
同bzoj1070修车;
每个厨师拆成$sum$(菜品总数)个点,每种菜品向厨师点连边,第$i$种菜品与第$j$名厨师的第$k$个点间的边流量为$1$,费用为$k*cost[i][j]$;
源点连每种菜品,流量$1$、费用$0$,厨师点连汇点,流量$1$,费用$0$,跑最小费用最大流;
然后GG;
看了题以为跟修车一样就十分naive地尝试写了一下,发现有300w条边,扑街;
注意到向某厨师的第$k+1$个点连边的费用一定大于第$k$个点,因此可以考虑动态加边,在某厨师的第$k$个点被选择之后再连第$k+1$个点的边;
AC GET☆DAZE
↓代码
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<cmath> 6 #include<algorithm> 7 #include<vector> 8 #include<queue> 9 #define N 100039 10 #define M 2000039 11 #define inf 0x7fffffff 12 #define ll long long 13 using namespace std; 14 struct edge 15 { 16 int from,to,next,cap,co; 17 }net[2*M]; 18 int n,m,ans=0,S,T; 19 int head[2*M],dis[N],pre[N],tot=-1,maxflow,mincost; 20 int num[139],sum,cost[139][139]; 21 bool inq[N]={0}; 22 void add(int i,int j,int k,int c) 23 { 24 net[++tot]=(edge){i,j,head[i],k,c},head[i]=tot; 25 net[++tot]=(edge){j,i,head[j],0,-c},head[j]=tot; 26 } 27 bool spfa() 28 { 29 queue<int> que; 30 int a,b,sn,sl; 31 for(a=S;a<=T;a++) 32 { 33 dis[a]=inf; 34 } 35 que.push(S); 36 dis[S]=0,inq[S]=1; 37 while(!que.empty()) 38 { 39 a=que.front(),que.pop(); 40 for(b=head[a];b!=-1;b=net[b].next) 41 { 42 sn=net[b].to,sl=net[b].co; 43 if(net[b].cap && dis[a]+sl<dis[sn]) 44 { 45 dis[sn]=dis[a]+sl; 46 pre[sn]=b; 47 if(!inq[sn]) 48 { 49 inq[sn]=1; 50 que.push(sn); 51 } 52 } 53 } 54 inq[a]=0; 55 } 56 return dis[T]^inf; 57 } 58 int mcf() 59 { 60 int flow=inf,a,i,j,k; 61 for(a=pre[T];a!=-1;a=pre[net[a].from]) 62 { 63 flow=min(flow,net[a].cap); 64 } 65 for(a=pre[T];a!=-1;a=pre[net[a].from]) 66 { 67 mincost+=flow*net[a].co; 68 net[a].cap-=flow,net[a^1].cap+=flow; 69 } 70 i=net[pre[T]].from,j=(i-n)/m+2,k=i+m; 71 a=i=(i-n)%m; 72 if(!a) 73 { 74 i=m,j--; 75 } 76 if(k<T) 77 { 78 add(k,T,1,0); 79 for(a=1;a<=n;a++) 80 { 81 add(a,k,1,j*cost[a][i]); 82 } 83 } 84 return flow; 85 } 86 void Sinogi() 87 { 88 maxflow=0,mincost=0; 89 while(spfa()) 90 { 91 maxflow+=mcf(); 92 } 93 } 94 int main() 95 { 96 memset(pre,-1,sizeof(pre)); 97 memset(head,-1,sizeof(head)); 98 int a,b,c,d; 99 scanf("%d%d",&n,&m); 100 S=0; 101 for(a=1;a<=n;a++) 102 { 103 scanf("%d",&b); 104 sum+=b; 105 add(S,a,b,0); 106 } 107 T=n+sum*m+1; 108 for(a=1;a<=n;a++) 109 { 110 for(b=1;b<=m;b++) 111 { 112 scanf("%d",&cost[a][b]); 113 add(a,n+b,1,cost[a][b]); 114 } 115 } 116 for(a=1;a<=m;a++) 117 { 118 add(n+a,T,1,0); 119 } 120 Sinogi(); 121 printf("%d",mincost); 122 return 0; 123 }