分析
看到这个题目,就想起了修车
建图时源点与菜品连流量为需求量费用为0的边,每个厨子在当时所做的菜的时刻与汇点连流量为1费用为0的边,菜品与每个厨子做的菜的时刻连流量为1费用为时间的边,但是这样会T掉,那怎么办,想到EK每次只跑一条增广路,那么可以动态加边,如果让厨子做完一道菜,那么就让它继续做菜,然后发现T了一个点,发现点数量开小了1,害得我改了几天
代码
#include <cstdio>
#include <cctype>
#include <cstring>
#include <queue>
#define min(a,b) ((a)<(b)?(a):(b))
#define rr register
using namespace std;
struct node{int y,w,f,next;}e[80101]; bool v[80101];
int n,m,ans,s,t,k=1,cnt,ls[80101],dis[80101],pre[80101],pay[41][101];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void add(int x,int y,int w,int f){
e[++k]=(node){y,w,f,ls[x]}; ls[x]=k;
e[++k]=(node){x,0,-f,ls[y]};ls[y]=k;
}
inline signed spfa(){
rr queue<int>q; memset(dis,127/3,sizeof(dis));
memset(v,0,sizeof(v));
dis[s]=0; v[s]=1; q.push(s);
while (q.size()){
rr int x=q.front(); q.pop();
for (rr int i=ls[x];i;i=e[i].next)
if (e[i].w>0&&dis[e[i].y]>dis[x]+e[i].f){
dis[e[i].y]=dis[x]+e[i].f;
pre[e[i].y]=i;
if (!v[e[i].y]) v[e[i].y]=1,q.push(e[i].y);
}
v[x]=0;
}
return dis[t]<707406378;
}
inline void update(){
rr int bk=t,minx=dis[0];
while (bk!=s) minx=min(minx,e[pre[bk]].w),bk=e[pre[bk]^1].y;
ans+=minx*dis[bk=t];
while (bk!=s) e[pre[bk]].w-=minx,e[pre[bk]^1].w+=minx,bk=e[pre[bk]^1].y;
bk=e[pre[t]^1].y;
add(bk+1,t,1,0);
for (rr int i=1;i<=n;++i)
add(i,bk+1,1,pay[i][(bk-n)/cnt+1]*((bk-n)%cnt+1));
}
signed main(){
n=iut(); m=iut();
for (rr int i=1;i<=n;++i){
rr int x=iut(); cnt+=x;
add(n+1,i,x,0);
}
s=n+cnt*m+2,t=s+1; ls[s]=ls[n+1]; ls[n+1]=0;
for (rr int i=3;i<=k;i+=2) if (e[i].y==n+1) e[i].y=s;
for (rr int i=1;i<=n;++i)
for (rr int j=1;j<=m;++j)
add(i,(j-1)*cnt+1+n,1,pay[i][j]=iut());
for (rr int j=1;j<=m;++j) add((j-1)*cnt+n+1,t,1,0);
while (spfa()) update();
return !printf("%d",ans);
}