BZOJ2879 NOI2012 美食节 费用流

题意:有N种菜,M位厨师,每位厨师做每种菜的时间不同。有K个人,每个人点ki个菜,求最小等待时间。

题解:

和BZOJ1070一个题,不过多了一个N,因此考虑优化。

显然决策倒数第K个菜的前提是倒数第K-1个菜已经决定完成,因此SPFA每搜出一条路径,就在这个厨师这里加边。说白了就是动点。

#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <climits>
#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN=600+2;
const int MAXV=200000+2;
const int MAXM=300000+2;
struct HASH{
    int u;
    HASH *next;
    HASH(){}
    HASH(int _u,HASH *_next):u(_u),next(_next){}
}*table[MAXV],mem[MAXM];
struct EDGE{
    int u,v,c,w;
    EDGE(){}
    EDGE(int _u,int _v,int _c,int _w):u(_u),v(_v),c(_c),w(_w){}
}e[MAXM];
int N,M,T,cnt=2,c[MAXN],w[MAXN][MAXN],cur[MAXV],d[MAXV],ans,tot;
bool flag[MAXV];
queue<int> q;

void Insert(int u,int v,int c,int w){
    table[u]=&(mem[cnt]=HASH(cnt,table[u])),e[cnt++]=EDGE(u,v,c,w);
    table[v]=&(mem[cnt]=HASH(cnt,table[v])),e[cnt++]=EDGE(v,u,0,-w);
}

bool SPFA(int s,int t){
    for(int i=s;i<=t;i++) d[i]=INT_MAX;
    d[s]=0,q.push(s),flag[s]=1;

    int x;
    while(!q.empty()){
        x=q.front(),q.pop();
        for(HASH *p=table[x];p;p=p->next)
            if(e[p->u].c && d[e[p->u].v]>d[x]+e[p->u].w){
                d[e[p->u].v]=d[x]+e[p->u].w,cur[e[p->u].v]=p->u;
                if(!flag[e[p->u].v]) flag[e[p->u].v]=1,q.push(e[p->u].v);
            }
        flag[x]=0;
    }
    return d[t]<INT_MAX;
}

void Find(int s,int t){
    int c=INT_MAX,a,x,y;
    for(int i=cur[t];i;i=cur[e[i].u]){
        c=min(c,e[i].c);
        if(!e[i].u) a=e[i].v,x=(a-1)/tot+1,y=a%tot+1;
    }
    for(int i=cur[t];i;i=cur[e[i].u]){
        e[i].c-=c,e[i^1].c+=c;
        ans+=e[i].w*c;
    }
    for(int i=1;i<=M;i++) Insert((x-1)*tot+y,N*tot+i,1,y*w[i][x]);
}

int main(){
    scanf("%d%d",&M,&N);
    for(int i=1;i<=M;i++){
       scanf("%d",c+i);
       tot+=c[i];
    }
    for(int i=1;i<=M;i++)
        for(int j=1;j<=N;j++)
            scanf("%d",&w[i][j]);

    T=N*tot+M+1;
    for(int i=1;i<=N*tot;i++) Insert(0,i,1,0);
    for(int i=1;i<=M;i++) Insert(N*tot+i,T,c[i],0);
    for(int i=1;i<=N;i++)
       for(int k=1;k<=M;k++)
          Insert((i-1)*tot+1,N*tot+k,1,w[k][i]);

    while(SPFA(0,T)) Find(0,T);
    printf("%d",ans);

    return 0;
}
View Code

 

posted @ 2017-02-28 22:58  WDZRMPCBIT  阅读(142)  评论(0编辑  收藏  举报