【費用流+動態加邊】[NOI2012]美食節

題目描述

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

 輸出僅一行包含一個整數,爲總等待時間的最小值。

Sample Input

3 2
3 1 1
5 7
3 6
8 9

Sample Output



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不會閒着,總等待時間更短。
可以證明,沒有更優的點餐方案。

【數據規模及約定】
對於100%的數據,n <= 40, m <= 100, p <= 800, ti,j <= 1000(其中p = ∑pi,即點菜同學的總人數)。
每組數據的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

HINT


Source


分析

建圖方式和【費用流】[BZOJ1070]/[HYSBZ1070]修車類似,不過這道題的數據範圍大多了,直接跑顯然會TLE。
影響spfa 效率的主要是邊的數量,那我們動態加邊好了。
一開始我們只將菜和廚師所拆出的最後一個點連邊。如果從一個廚師那裏增廣了,我們再將菜那個廚師拆成的前一個點連邊。我們可以保證,最短路一定在我們已經連了的邊中,所以不會出錯。

代碼

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define INF 0x7fffffff
#define MAXN 40
#define MAXM 100
#define MAXP 800
using namespace std;
queue<int>q;
void Read(int &x){
    char c;
    while(c=getchar(),c!=EOF)
        if(c>='0'&&c<='9'){
            x=c-'0';
            while(c=getchar(),c>='0'&&c<='9')
                x=x*10+c-'0';
            ungetc(c,stdin);
            return;
        }
}
int n,m,tm[MAXN+10][MAXM+10],S,T,tot,dist[MAXP*MAXM+MAXN+10],p,pp[MAXN+10];
bool vis[MAXP*MAXM+MAXN+10];
struct node{
    int v,wt,cap;
    node *next,*back;
}*adj[MAXP*MAXM+MAXN+10],edge[MAXP*MAXM*MAXN+10],*ecnt=edge,*pre[MAXP*MAXM+MAXN+10];
inline void addedge(int u,int v,int cap,int wt){
    node *p=++ecnt;
    p->v=v;
    p->wt=wt;
    p->cap=cap;
    p->next=adj[u];
    adj[u]=p;
    p=p->back=++ecnt;
    p->v=u;
    p->wt=-wt;
    p->cap=0;
    p->next=adj[v];
    adj[v]=p;
    p->back=ecnt-1;
}
bool spfa(){
    int i,u;
    for(i=1;i<=tot;i++)
        dist[i]=INF;
    dist[S]=0;
    q.push(S);
    while(!q.empty()){
        u=q.front();
        vis[u]=0;
        q.pop();
        for(node *p=adj[u];p;p=p->next)
            if(p->cap&&dist[p->v]>dist[u]+p->wt){
                pre[p->v]=p;
                dist[p->v]=dist[u]+p->wt;
                if(!vis[p->v])
                    q.push(p->v),vis[p->v]=1;
            }
    }
    return dist[T]!=INF;
}
inline int Get_id(int i,int j){
    return (i-1)*p+j+n;
}
inline int Get_cook(int id){
    return (id-n+p-1)/p;
}
inline int Get_dish(int id){
    return (id-n-1)%p+1;
}
void read(){
    Read(n),Read(m);
    int i,j;
    for(i=1;i<=n;i++)
        Read(pp[i]),p+=pp[i];
    S=p*m+n+1,T=tot=S+1;
    for(i=1;i<=n;i++)
        addedge(S,i,pp[i],0);
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            Read(tm[i][j]);
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            addedge(i,Get_id(j,p),1,tm[i][j]);
    for(j=1;j<=m;j++)
        addedge(Get_id(j,p),T,1,0);
}
int mcmf(){
    node *p;
    int delta,flow=0,cost=0,cook,i,dish,pos;
    while(flow< ::p&&spfa()){
        delta=INF;
        for(p=pre[T];p;p=pre[p->back->v])
            delta=min(delta,p->cap);
        for(p=pre[T];p;p=pre[p->back->v]){
            p->cap-=delta;
            p->back->cap+=delta;
            cost+=p->wt*delta;
        }
        flow+=delta;
        pos=pre[T]->back->v-1;
        cook=Get_cook(pos);
        dish=Get_dish(pos);
        for(i=1;i<=n;i++)
            addedge(i,pos,1,tm[i][cook]*(::p-dish+1));
        addedge(pos,T,1,0);
    }
    return cost;
}
int main()
{
    read();
    printf("%d\n",mcmf());
}
發佈了171 篇原創文章 · 獲贊 64 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章