P2050 [NOI2012]美食节
题目描述
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)
Solution:
本题也是调的心态炸裂,几小时后才发现多建了好多边(两天考试才rank5,不满不满,心态差啊1-1!)。
上题修车,本题就是加强版,所以就不多讲思路了,直接说优化。
唯二不同的地方就是:1、每种菜有多个; 2、数据范围变大了。
针对 1、比较好搞,建图时$s$到每种菜的流量设为该种菜的个数就好了。
针对 2、有点(好)麻烦,因为最多$40*800*100$条边、$800*100$个点,再EK跑费用流是肯定会T的。优化方法是动态加边,因为每次费用为$w_i*k$,贪心的想到,第$1$次增广一定会是某个厨师倒数第$1$次做的情况(这样肯定费用最少),所以我们可以先只把所有厨师的倒数第$1$次做的状态建点连边,然后每增广一次,就对增广路上的那个厨师的状态往前移(费用变大),因为最多增广$p\leq 800$次,每次增广多$n$条边,图上顶多有$(m+p)*n$条边,所以最坏复杂度是$O(k*p*n*(m+p))$,这样就能过了.~^_^~.。
代码:
/*Code by 520 -- 8.27*/ #include<bits/stdc++.h> #define il inline #define ll long long #define RE register #define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++) #define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--) #define debug printf("%d %s\n",__LINE__,__FUNCTION__) using namespace std; const int N=100005,M=5000005,inf=0x7fffffff; int n,m,maxn[N],pre[N],s,t,lala; int dis[N],to[M],net[M],w[M],c[M],h[N],cnt=1; int maxf,maxc,num[N],mp[50][105],bl[105][805],ppx[N]; bool vis[N]; il void add(int u,int v,int fl,int co){ to[++cnt]=v,net[cnt]=h[u],w[cnt]=fl,c[cnt]=co,h[u]=cnt; to[++cnt]=u,net[cnt]=h[v],w[cnt]=0,c[cnt]=-co,h[v]=cnt; } queue<int>q; il bool spfa(){ For(i,1,t) dis[i]=inf; dis[s]=0,maxn[s]=inf,q.push(s); while(!q.empty()){ RE int u=q.front();q.pop();vis[u]=0; for(RE int i=h[u];i;i=net[i]) if(w[i]&&dis[to[i]]>dis[u]+c[i]){ dis[to[i]]=dis[u]+c[i],pre[to[i]]=i, maxn[to[i]]=min(maxn[u],w[i]); if(!vis[to[i]])vis[to[i]]=1,q.push(to[i]); } } return dis[t]!=inf; } il void update(){ int p=t; while(p!=s){ RE int i=pre[p]; w[i]-=maxn[t],w[i^1]+=maxn[t]; p=to[i^1]; } p=to[pre[t]^1];num[p+1]=num[p]+1; add(p+1,t,1,0); For(i,1,n) add(i,p+1,1,mp[i][ppx[p]]*num[p+1]); maxf+=maxn[t],maxc+=maxn[t]*dis[t]; } il void init(){ scanf("%d%d",&n,&m); int val,tot=n; For(i,1,n) scanf("%d",&val),add(s,i,val,0),lala+=val; For(i,1,m) { num[tot+1]=1; For(j,1,lala) bl[i][j]=++tot,ppx[tot]=i; } t=tot+1; For(i,1,n) For(j,1,m) scanf("%d",&mp[i][j]), add(i,bl[j][1],1,mp[i][j]); For(i,1,m) add(bl[i][1],t,1,0); while(spfa())update(); cout<<maxc; } int main(){ init(); return 0; }