1. 程式人生 > >[NOI2012]美食節,P2050,最小費用最大流

[NOI2012]美食節,P2050,最小費用最大流

正題

      這一題是[SCOI2007]修車的加強版。(先看修車再做這題哦!)

      首先是每種菜可能有多個人需求。

      怎麼解決?我們把菜放左邊,人放右邊,然後左邊把流量設成p_i就可以了。

      由於我們每次增廣的流量是1,所以時間複雜度是很高的,是O(P^2mk),k為SPFA的不定常數。

      爆炸,而且建邊的空間和時間我們也承受不起。

      我們再來考慮動態加邊。

      一個廚師先選倒數第1道菜,再選倒數第2道菜。因為倒數第1道菜只用被算1次,倒數第二道菜會被算2次。

      我們可以先把每一種菜連向每個廚師的倒數第一道菜。

      假如a廚師的倒數第1道菜被選了,那麼就把a廚師的倒數第二道菜開放。

      以此類推,一直到所有菜都被選完了。

// luogu-judger-enable-o2
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;

int n,m;
int num[810];
int p[50][110];
struct edge{
    int x,y,next,c,cos;
}s[9000010];
int first[90010],d[90010],mmin[90010],last[90010],len=1;
bool tf[90010];
int begin,end;
int tot=0;
queue<int> f;

void ins(int x,int y,int c,int cos){
    len++;s[len]=(edge){x,y,first[x],c,cos};first[x]=len;
    len++;s[len]=(edge){y,x,first[y],0,-cos};first[y]=len;
}

bool SPFA(int&flow,int&cost){
    memset(tf,false,sizeof(tf));tf[begin]=true;
    memset(d,63,sizeof(d));d[begin]=0;
    mmin[begin]=1e9;
    f.push(begin);
    while(!f.empty()){
        int x=f.front();f.pop();tf[x]=false;
        for(int i=first[x];i!=0;i=s[i].next){
            int y=s[i].y;
            if(d[y]>d[x]+s[i].cos && s[i].c>0){
                d[y]=d[x]+s[i].cos;mmin[y]=min(mmin[x],s[i].c);last[y]=i;
                if(!tf[y]){
                    tf[y]=true;
                    f.push(y);
                }
            }
        }
    }
    if(d[end]==d[end+1]) return false;
    flow+=mmin[end];
    cost+=mmin[end]*d[end];
    int now=end;
    while(now!=begin){
        s[last[now]].c-=mmin[end];s[last[now]^1].c+=mmin[end];
        now=s[last[now]].x;
    }
    now=s[last[end]].x;
    int x=(now-n)/tot+1,y=(now-n)%tot+1;
	if((now-n)%tot!=0) for(int k=1;k<=n;k++) ins(k,now+1,1,p[k][x]*y);
    return true;
}

void MCMF(){
    int flow=0,cost=0;
    while(SPFA(flow,cost));
    printf("%d",cost);
}

int main(){
    scanf("%d %d",&n,&m);
    int x;
    for(int i=1;i<=n;i++){
        scanf("%d",&x);
        tot+=x;
        ins(begin,i,x,0);
    }
	end=n+tot*m+1;
    for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&p[i][j]);
    for(int j=1;j<=m;j++) for(int k=1;k<=n;k++) ins(k,n+(j-1)*tot+1,1,p[k][j]*1);
    for(int i=n+1;i<end;i++) ins(i,end,1,0);
    MCMF();
}