2879: [Noi2012]美食节
2013年3月31日 15:11
http://www.lydsy.com/JudgeOnline/problem.php?id=2879
RunID | User | Problem | Result | Memory | Time | Language | Code_Length | Submit_Time |
380608 | Liu_Ts | 2879 | Accepted | 5536 kb | 4296 ms | C++ | 1539 B | 2013-03-31 14:48:33 |
题意
有n道菜,每道菜需要p[i]份,m个厨师,第j个厨师做第i到菜需要时间t[i][j],求做完所有菜,所有人等待的最小总时间。
题解
和之前有个usaco一样,
令Sp=Σp[i]
把每个厨师拆成Sp个点,
每道菜向一个点连边,费用为这个厨师倒数第p道做这个菜,对所有人产生的影响,即p*t[i][j];(会对当前这个人以及厨师接下来所做菜的几个人产生影响)
但是这道题范围太大,
所以动态加边,
每次增广完后,找到一个当前还没有被增广的厨师,拆n个点,继续增广。
具体看程序理解。
#include<cstring> #include<cstdio> using namespace std; const int N=1000,M=100000; int u[M],v[M],w[M],c[M],next[M],head[N],k=1; int n,m,q[M],d[N],pre[N],sp,p[41],t[41][101],cnt[101],last[101]; bool vis[N]; void add(int x,int y,int z,int zz) { u[++k]=x;v[k]=y;w[k]=z;c[k]=zz;next[k]=head[x];head[x]=k; u[++k]=y;v[k]=x;w[k]=0;c[k]=-zz;next[k]=head[y];head[y]=k; } void spfa(int s,int t) { memset(d,127,sizeof(d)); d[s]=0;vis[s]=true; int l=0,r=1;q[r]=s; while(l<r) { l=l%M+1; for(int i=head[q[l]];i;i=next[i]) if(w[i]&&d[v[i]]>d[u[i]]+c[i]) { d[v[i]]=d[u[i]]+c[i];pre[v[i]]=i; if(!vis[v[i]]) { vis[v[i]]=true; r=r%M+1;q[r]=v[i]; } } vis[q[l]]=false; } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",p+i); sp+=p[i]; } int S=n+m+sp+1,T=S+1; for(int i=1;i<=n;i++) { add(S,i,p[i],0); for(int j=1;j<=m;j++) { scanf("%d",&t[i][j]); add(i,n+j,1,t[i][j]); } } for(int i=1;i<=m;i++) { cnt[i]=1; add(n+i,T,1,0); last[i]=k; } int ans=0; for(;sp;sp--) { spfa(S,T);ans+=d[T]; for(int i=T;i!=S;i=u[pre[i]]) w[pre[i]]--,w[pre[i]^1]++; int j; for(j=1;j<=m&&w[last[j]-1];j++); cnt[j]++; for(int i=1;i<=n;i++) add(i,n+m+sp,1,t[i][j]*cnt[j]); add(n+m+sp,T,1,0); last[j]=k; } printf("%d\n",ans); }