【NOI2012】美食节

Description

CZ 市为了欢迎全国各地的同学,特地举办了一场盛大的美食节。

作为一个喜欢尝鲜的美食客,小 M 自然不愿意错过这场盛宴。他很快就尝遍了美食节所有的美食。然而, 尝鲜的欲望是难以满足的。尽管所有的菜品都很可口,厨师做菜的速度也很快,小 M 仍然觉得自己桌上没有已经摆在别人餐桌上的美食是一件无法忍受的事情。于是小 M 开始研究起了做菜顺序的问题,即安排一个做菜的顺序使得同学们的等待时间最短。

小 M 发现,美食节共有 n n 种不同的菜品。每次点餐,每个同学可以选择其中的一个菜品。总共有 m 个厨师来制作这些菜品。当所有的同学点餐结束后,菜品的制作任务就会分配给每个厨师。然后每个厨师就会同时开始做菜。 厨师们会按照要求的顺序进行制作,并且每次只能制作一人份。

此外,小 M 还发现了另一件有意思的事情——虽然这 m m 个厨师都会制作全部的 n 种菜品,但对于同一菜品,不同厨师的制作时间未必相同。他将菜品用 1,2,,n 1 , 2 , … , n 依次编号,厨师用 1,2,,m 1 , 2 , … , m 依次编号,将第 j j 个厨师制作第 i 种菜品的时间记为 Ti,j

小 M 认为:每个同学的等待时间为所有厨师开始做菜起,到自己那份菜品完成为止的时间总长度。换句话说,如果一个同学点的菜是某个厨师做的第 k k 道菜,则他的等待时间就是这个厨师制作前 k 道菜的时间之和。 而总等待时间为所有同学的等待时间之和。

现在,小 M 找到了所有同学的点菜信息——有 ai a i 个同学点了第 i i 种菜品(i=1,2,,n) 。他想知道的是最小的总等待时间是多少。

Input

输入文件的第 1 1 行包含两个正整数 n m m ,表示菜品的种数和厨师的数量。

2 行包含 n n 个正整数,其中第 i 个数为 ai a i ,表示点第 i i 种菜品的人数。

接下来有 n 行,每行包含 m m 个非负整数,这 n 行中的第 i i 行的第 j 个数为 Ti,j T i , j

表示第 j  j   个厨师制作第 i i 种菜品所需的时间。

输入文件中每行相邻的两个数之间均由一个空格隔开,行末均没有多余空格。

Output

输出仅一行包含一个整数,为总等待时间的最小值。

Data Constraint

对于 100% 的数据, n40,m100,a800,ti,j1000 n ≤ 40 , m ≤ 100 , a ≤ 800 , t i , j ≤ 1000 (其中 a=ai  a = ∑ a i   ,即点菜同学的总人数)。

Sample Input

3 2

3 1 1

5 7

3 6

8 9

Sample Output

47

Hint

厨师 1 1 先制作 1 份菜品 2 2 ,再制作 2 份菜品 1 1 。点这 3 道菜的 3 3 个同学的等待时间分别为 33+5=83+5+5=13

厨师 2 2 先制作 1 份菜品 1 1 ,再制作 1 份菜品 3 3 。点这 2 道菜的 2 2 个同学的等待时间分别为 77+9=16

总等待时间为 3+8+13+7+16=47 3 + 8 + 13 + 7 + 16 = 47

虽然菜品 1 1 和菜品 3 由厨师 1 1 制作更快,如果这些菜品都由厨师 1 制作,总等待时间反而更长。如果按上述的做法,将 1 1 份菜品 1 1 1 份菜品 3 调整到厨师 2 2 制作,这样厨师 2 不会闲着,总等待时间更短。

可以证明,没有更优的点餐方案。

Solution

很明显是一道网络流题目,先阐述连边方法:
连边
对于每个厨师,后做的菜不会影响前面做的菜,所以先从倒数第一时刻连边。
为什么不能一下把所有时刻的边连好呢?因为边数太多啦!(屈指一算大约有 n(ma[i]+1) n ∗ ( m ∗ ∑ a [ i ] + 1 ) 条边!)
所以思路就是,如果某厨师没做第  i    i   道菜,那他就不可能做第  i+1    i + 1   道菜。每做一道菜,就为前一道菜(时刻)连边继续做流,且费用为 Citi,j C i ∗ t i , j Ci C i 为倒数第几道。

这里写图片描述

Code

var             
        s,ans,n,m,tot,p,i,j:longint;
        dt,xx,yy,cont1,cont2,next,g,path:array[0..2000000] of longint;
        vis:array[0..100005] of boolean;
        dis:array[0..100005] of  longint;
        a:array[1..100] of longint;
        t:array[1..100,1..100] of longint;
procedure make(x,y,liu,fei:longint);
begin
        inc(tot);
        xx[tot]:=x;
        yy[tot]:=y;
        cont1[tot]:=liu;
        cont2[tot]:=fei;
        next[tot]:=g[x];
        g[x]:=tot;
end;
procedure init;
var     i,j:longint;
begin
    readln(n,m);
    for i:=1 to n do begin
        read(a[i]);
        inc(p,a[i]);
    end;
        for i:=1 to m do begin
                make(0,(i-1)*p+1,1,0);
                make((i-1)*p+1,0,0,0);
        end;
    readln;
    for i:=1 to n do begin
        for j:=1 to m do begin
            read(t[i,j]);
            make(p*(j-1)+1,p*m+i,1,t[i,j]);
            make(p*m+i,p*(j-1)+1,0,-t[i,j]);
        end;
        make(p*m+i,p*m+n+1,a[i],0);
        make(p*m+n+1,p*m+i,0,0);
        readln;
    end;
end;
function bfs:boolean;
var l,r,i,j,x,y,mm:longint;
begin
    dt[1]:=s;
    fillchar(dis,sizeof(dis),127);
    fillchar(vis,sizeof(vis),false);
    mm:=dis[1];
    dis[0]:=1;
    l:=0;r:=1;
    vis[0]:=true;
    while l<r do begin
        inc(l);
        if l=100000 then l:=0;
        x:=dt[l];
        i:=g[x];
        while i<>0 do begin
            y:=yy[i];
            if (cont2[i]+dis[x]<dis[y]) and (cont1[i]<>0) then begin
                dis[y]:=dis[x]+cont2[i];
                path[y]:=i;
                if not vis[y] then begin
                    inc(r);
                    if r=100000 then r:=0;
                    dt[r]:=y;
                    vis[y]:=true;
                end;
            end;
            i:=next[i];
        end;
        vis[x]:=false;
    end;
    if dis[m*p+n+1]=mm then exit(false) else exit(true);
end;
procedure fl;
var     i,x,y,v,qv,ci:longint;
begin

        x:=path[m*p+n+1];
        v:=maxlongint;
        while x<>0 do begin
            if v>cont1[x] then v:=cont1[x];
            if xx[x]=0 then begin
                y:=yy[x];
                qv:=(y-1) div p+1;
                ci:=y mod p+1;
            end;
            x:=path[xx[x]];
        end;
        x:=path[m*p+n+1];
        while x<>0 do begin
            dec(cont1[x],v);
            inc(cont1[x xor 1],v);
            ans:=ans+cont2[x]*v;
            x:=path[xx[x]];
        end;
            make(0,(qv-1)*p+ci,1,0);
            make((qv-1)*p+ci,0,0,0);
        for i:=1 to n do begin
            make((qv-1)*p+ci,m*p+i,1,ci*t[i,qv]);
            make(m*p+i,(qv-1)*p+ci,0,-ci*t[i,qv]);
        end;
end;
begin
    tot:=1;
    init;
    while bfs do fl;
    writeln(ans);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值