建图和修车是一样的,不过这题需要动态加边。
注意这题费用流必须跑一条路加一条路,不能跑多条路后加边
因为如果跑多条路后加边可能导致跑了不是最优的解,然后加了一条边权较小的边出现负环。
不过如果每次跑一条路加一条路可以保证每次取的都是最优解。
#include <bits/stdc++.h>
using namespace std;
#define inf 1e9
#define N 11000
#define M 1100000
int n,m,tot,cnt,S,T,ans,sum,sp;
int p[51],tim[110][51],veg[51];
int head[N],nex[M],to[M],val[M],cot[M],use[110];
int f[N],inq[N],pre[N],deep[N];
queue<int>q;
void add(int x,int y,int v,int c)
{
tot++;
nex[tot]=head[x];head[x]=tot;
to[tot]=y;val[tot]=v;cot[tot]=c;
}
void ade(int x,int y,int v,int c)
{add(x,y,v,c);add(y,x,0,-c);}
int dfs(int x,int mv)
{
if(x==S)return mv;
int tmp=dfs(to[pre[x]^1],min(mv,val[pre[x]]));
val[pre[x]]-=tmp;
val[pre[x]^1]+=tmp;
return tmp;
}
int bfs()
{
memset(f,0x3f,sizeof(f));
memset(deep,-1,sizeof(deep));
f[S]=0;q.push(S);deep[S]=0;
while(!q.empty())
{
int tmp=q.front();q.pop();
inq[tmp]=0;
for(int i=head[tmp];i;i=nex[i])
if(f[to[i]]>f[tmp]+cot[i]&&val[i])
{
f[to[i]]=f[tmp]+cot[i];
pre[to[i]]=i;deep[to[i]]=deep[tmp]+1;
if(!inq[to[i]])
{
inq[to[i]]=1;
q.push(to[i]);
}
}
}
return dfs(T,inf)*f[T];
}
int main()
{
//freopen("tt.in","r",stdin);
tot=1;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&p[i]),sp+=p[i];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&tim[j][i]);
cnt=m;S=++cnt;T=++cnt;
for(int i=1;i<=n;i++)
{
veg[i]=++cnt;
ade(veg[i],T,p[i],0);
}
for(int i=1;i<=m;i++)
ade(S,i,inf,0);
for(int i=1;i<=m;i++)
{
cnt++,ade(i,cnt,1,0);
for(int j=1;j<=n;j++)
ade(cnt,veg[j],1,tim[i][j]);
}
while(sum!=sp)
{
ans+=bfs();
for(int i=head[S];i;i=nex[i])
if(val[i]+use[to[i]]!=inf)
{
use[to[i]]++;sum++;
cnt++,ade(to[i],cnt,1,0);
for(int j=1;j<=n;j++)
ade(cnt,veg[j],1,tim[to[i]][j]*(use[to[i]]+1));
}
}
printf("%d\n",ans);
return 0;
}