美食节
题解
其实还是很简单的。
看到这道题应该很容易想到费用流。
由于做菜有时间顺序,我们考虑将每个厨师的每一道菜单独建点。对于
i
i
i号厨师的导师第
j
j
j号菜,向汇点连一条流量为1边权为0的边,限制这样的菜只能由一个。再将所有菜连向这个点,对与菜
k
k
k边权为
j
⋅
t
i
,
k
j\cdot t_{i,k}
j⋅ti,k,表示第
k
k
k道菜的价值。
至于每道菜的需求,只需要向这道菜连它需求的流量即可,保证最多只会有这么多道菜被做。
但很明显,虽然这样的做法可以得到答案,但图还是太大了,明显会T,考虑优化。
我们明显不能将每个厨师的每一道菜都建出来,因为总菜数只有那么多,而这样做会有许多点根本没用到。
所以我们考虑动态加点。很明显,EK每次增广路径有且只会增广一条路径,经过一道菜。
我们可以在每次增广路径上找到增广的菜,单独给做这道菜的厨师给它拓展一道菜,再跑增广路径。因为只有这个厨师的下一道菜会给下一次操作产生影响,其他厨师这道菜都没做,显然不会直接做下一道菜。
所以我们只需要在每次增广完后动态加点,当不能再增广时退出即可。
这样一来总点数为
∑
i
=
1
n
p
i
+
n
+
m
\sum_{i=1}^{n}p_{i}+n+m
∑i=1npi+n+m,边数为
n
(
∑
i
=
1
n
p
i
+
m
)
n(\sum_{i=1}^{n}p_{i}+m)
n(∑i=1npi+m)。完全足够。
时间复杂度
O
(
(
∑
i
=
1
n
p
i
+
n
+
m
)
3
)
O\left((\sum_{i=1}^{n}p_{i}+n+m)^3\right)
O((∑i=1npi+n+m)3)?反正跑不满就对了。
源码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN 105
#define MAXM 90005
#define MAXX 10000005
#define reg register
typedef long long LL;
typedef pair<int,LL> pii;
const int INF=0x7f7f7f7f;
const LL inf=0x7f7f7f7f7f7f;
template<typename _T>
inline void read(_T &x){
_T f=1;x=0;char s=getchar();
while('0'>s||'9'<s){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
int n,m,head[MAXM],tot,pre[MAXM],pw[MAXM],cnt,idx;LL dis[MAXM];
int S,T,fl[MAXM],sum,p[MAXN],ck[MAXM],fd[MAXM],mk[MAXN][MAXN];
queue<int> q;bool vis[MAXM];
struct edge{int to,nxt,flow;LL paid;int op;}e[MAXX];
struct ming{int a,b,s,t;}s[MAXN];
inline void addEdge(const int u,const int v,const int f,const LL w){e[++tot]=(edge){v,head[u],f,w};head[u]=tot;}
inline void addedge(const int u,const int v,const int f,const LL w){addEdge(u,v,f,w);e[tot].op=tot+1;addEdge(v,u,0,-w);e[tot].op=tot-1;}
inline pii sakura(){
int mf=0;LL res=0;
while(1){
while(!q.empty())q.pop();
for(reg int i=1;i<=cnt;++i)dis[i]=inf,fl[i]=pre[i]=pw[i]=0,vis[i]=0;
q.push(S);dis[S]=0;vis[S]=1;fl[S]=INF;
while(!q.empty()){
const int u=q.front();q.pop();vis[u]=0;if(!fl[u])continue;
for(reg int i=head[u];i;i=e[i].nxt){
const int v=e[i].to;
if(dis[v]>dis[u]+1ll*e[i].paid&&e[i].flow>0){
dis[v]=dis[u]+1ll*e[i].paid;pre[v]=u;pw[v]=i;
if(!vis[v])q.push(v),vis[v]=1,fl[v]=min(fl[u],e[i].flow);
}
}
}
if(!pre[T])break;mf+=fl[T];res+=1ll*fl[T]*dis[T];int id=pre[T];
for(reg int i=T;i^S;i=pre[i])e[pw[i]].flow-=fl[T],e[e[pw[i]].op].flow+=fl[T];
addedge(++idx,T,1,0);ck[idx]=ck[id];fd[idx]=fd[id]+1;
for(reg int i=1;i<=n;++i)addedge(i,idx,1,1ll*fd[idx]*mk[ck[idx]][i]);
}
return make_pair(mf,res);
}
signed main(){
read(n);read(m);
for(reg int i=1;i<=n;++i)read(p[i]),sum+=p[i];
S=sum+m+n+1;cnt=T=sum+m+n+2;
for(reg int i=1;i<=n;++i)addedge(S,i,p[i],0),++idx;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)read(mk[j][i]);
for(reg int i=1;i<=m;++i){
addedge(++idx,T,1,0);ck[idx]=i;fd[idx]=1;
for(reg int j=1;j<=n;++j)
addedge(j,idx,1,1ll*mk[i][j]);
}
pii ans=sakura();printf("%lld\n",ans.second);
return 0;
}