这一题是[SCOI2007]修车的加强版。(先看修车再做这题哦!)
首先是每种菜可能有多个人需求。
怎么解决?我们把菜放左边,人放右边,然后左边把流量设成就可以了。
由于我们每次增广的流量是1,所以时间复杂度是很高的,是,k为SPFA的不定常数。
爆炸,而且建边的空间和时间我们也承受不起。
我们再来考虑动态加边。
一个厨师先选倒数第1道菜,再选倒数第2道菜。因为倒数第1道菜只用被算1次,倒数第二道菜会被算2次。
我们可以先把每一种菜连向每个厨师的倒数第一道菜。
假如a厨师的倒数第1道菜被选了,那么就把a厨师的倒数第二道菜开放。
以此类推,一直到所有菜都被选完了。
// luogu-judger-enable-o2
#include
#include
#include
#include
#include
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 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