题目描述
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)。他想知道的是最小的总等待时间是多少。
输入输出格式
输入格式:
输入文件的第1行包含两个正整数n和m,表示菜品的种数和厨师的数量。 第2行包含n个正整数,其中第i个数为pi,表示点第i种菜品的人数。 接下来有n行,每行包含m个非负整数,这n行中的第i行的第j个数为ti,j,表示第j个厨师制作第i种菜品所需的时间。 输入文件中每行相邻的两个数之间均由一个空格隔开,行末均没有多余空格。
输出格式:
输出仅一行包含一个整数,为总等待时间的最小值。
输入输出样例
3 2 3 1 1 5 7 3 6 8 9
47
说明
厨师1先制作1份菜品2,再制作2份菜品1。点这3道菜的3个同学的等待时间分别为3,3+5=8,3+5+5=13。
厨师2先制作1份菜品1,再制作1份菜品3。点这2道菜的2个同学的等待时间分别为7,7+9=16。
总等待时间为3+8+13+7+16=47。
虽然菜品1和菜品3由厨师1制作更快,如果这些菜品都由厨师1制作,总等待时间反而更长。如果按上述的做法,将1份菜品1和1份菜品3调整到厨师2制作,这样厨师2不会闲着,总等待时间更短。
可以证明,没有更优的点餐方案。 每组数据的n、m和p值如下:
测试点编号 | n | m | p |
---|---|---|---|
1 | n = 5 | m = 5 | p = 10 |
2 | n = 40 | m = 1 | p = 400 |
3 | n = 40 | m = 2 | p = 300 |
4 | n = 40 | m = 40 | p = 40 |
5 | n = 5 | m = 40 | p = 100 |
6 | n = 10 | m = 50 | p = 200 |
7 | n = 20 | m = 60 | p = 400 |
8 | n = 40 | m = 80 | p = 600 |
9 | n = 40 | m = 100 | p = 800 |
10 | n = 40 | m = 100 | p = 800 |
对于100%的数据,n <= 40, m <= 100, p <= 800, ti,j <= 1000 (其中p = ∑pi)
题解
- 这题显然可以费用流
- 考虑如何建图,首先从源点向每种菜连一条流量为p[i]、费用为0的边
- 将每个厨师拆成所有需要的菜数个数个点,然后连一条流量为1、费用为该厨师做该饭菜的时间
- 仔细算一算,边数是有3200000,那么就动态开边
代码
1 #include2 #include 3 #include 4 #include 5 #define inf 0x7fffffff 6 #define N 100005 7 #define M 7000000 8 using namespace std; 9 int n,m,sum,ans,cnt,head[N],vis[N],pre[N],dis[N],a[50][120],p[50],s,t;10 struct edge{ int to,from,op,c,v,last; }e[M];11 queue Q;12 void insert(int x,int y,int a,int b)13 {14 e[++cnt].to=y; e[cnt].last=x; e[cnt].from=head[x]; e[cnt].c=a; e[cnt].v=b; e[cnt].op=cnt+1; head[x]=cnt;15 e[++cnt].to=x; e[cnt].last=y; e[cnt].from=head[y]; e[cnt].c=0; e[cnt].v=-b; e[cnt].op=cnt-1; head[y]=cnt;16 }17 bool spfa()18 {19 for (int i=s;i<=t;i++) dis[i]=inf/3,vis[i]=pre[i]=0;20 while (!Q.empty()) Q.pop();21 Q.push(s),vis[s]=1,dis[s]=0;22 while (!Q.empty())23 {24 int u=Q.front(); Q.pop();25 for (int i=head[u];i;i=e[i].from)26 if (e[i].c&&dis[u]+e[i].v 0)45 {46 insert(e[pre[l]].last+1,t,1,0);47 int u=(e[pre[l]].last-n)/sum+1,v=(e[pre[l]].last-n)%sum+1;48 for (int i=1;i<=n;i++) insert(i,e[pre[l]].last+1,1,a[i][u]*v);49 }50 l=e[pre[l]].last;51 }52 }53 int main()54 {55 scanf("%d%d",&n,&m);56 for (int i=1;i<=n;i++) scanf("%d",&p[i]),sum+=p[i];57 for (int i=1;i<=n;i++)58 for (int j=1;j<=m;j++)59 scanf("%d",&a[i][j]);60 s=0,t=n+m*sum+1;61 for (int i=1;i<=n;i++) insert(s,i,p[i],0);62 for (int i=1;i<=m;i++)63 {64 for (int j=1;j<=n;j++) insert(j,n+(i-1)*sum+1,1,a[j][i]);65 insert(n+(i-1)*sum+1,t,1,0);66 }67 while (spfa()) mcf();68 printf("%d",ans);69 }