分析:
看完题面,我就想到了这道题
实际上,这道题的建图就是这样:
写完之后 —> 60
(一定要算准空间)
边的数量比较多,所以如果一口气把所有的边都连上的话,时间是承受不了的
由于我们跑一次spfa只能找出一次增广路,所以我们可以暂时不连不需要的边
一开始,我们把所有厨师做倒数第1道菜与所有菜连好,然后找一条增广路,
这条增广路上一定经过了一个点,表示第j个厨师做倒数第1道菜,
于是我们添加点(第j个厨师做倒数第2道菜),与汇点和所有菜连边,以此类推
这样每次spfa的时候,需要的边都被连上了
tip
点的编号要好好计算
空间一定要算对(会T)
结点个数:n+m*p
边数:2*(n+(n+1)*m*p)
//这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#define ll long long
using namespace std;
const int INF=0x33333333;
const int N=8000000;
struct node{
int x,y,v,c,nxt;
};
node way[N];
int st[100000],s,t,dis[100000],pre[100000],a[43][105],tot=-1,C[43];
int n,m,tt,pos,wh;
bool in[100000];
void add(int u,int w,int z,int cc)
{
tot++;
way[tot].x=u;way[tot].y=w;way[tot].v=z;way[tot].c=cc;way[tot].nxt=st[u];st[u]=tot;
tot++;
way[tot].x=w;way[tot].y=u;way[tot].v=0;way[tot].c=-cc;way[tot].nxt=st[w];st[w]=tot;
}
int spfa(int s,int t)
{
memset(in,0,sizeof(in));
memset(dis,0x33,sizeof(dis));
dis[s]=0; in[s]=1;
queue<int> Q;
Q.push(s);
while (!Q.empty())
{
int now=Q.front(); Q.pop();
for (int i=st[now];i!=-1;i=way[i].nxt)
if (way[i].v&&way[i].c+dis[now]<dis[way[i].y])
{
dis[way[i].y]=way[i].c+dis[now];
pre[way[i].y]=i;
if (!in[way[i].y])
{
Q.push(way[i].y);
in[way[i].y]=1;
}
}
in[now]=0;
}
return dis[t]!=INF;
}
int fan(int bh)
{
bh-=n;
wh=(bh%tt)+1;
pos=bh/tt;
if (bh%tt!=0) pos++;
}
ll doit()
{
ll ans=0;
spfa(s,t);
int sum=INF;
for (int i=t;i!=s;i=way[pre[i]].x)
sum=min(sum,way[pre[i]].v);
ans+=(ll)sum*dis[t];
for (int i=t;i!=s;i=way[pre[i]].x)
way[pre[i]].v-=sum,
way[pre[i]^1].v+=sum;
fan(way[pre[t]].x);
return ans;
}
ll solve()
{
ll ans=0;
s=0;
int cnt=0,cnt2=0;
for (int i=1;i<=n;i++)
add(s,i,C[i],0);
t=tt*m+n+1;
for (int i=1;i<=m;i++)
add((i-1)*tt+1+n,t,1,0);
for (int i=1;i<=n;i++) //所有菜向每一个厨师的倒数第一道连边
for (int j=1;j<=m;j++)
{
int num=n+(j-1)*tt+1;
add(i,num,1,a[i][j]);
}
ans+=doit();
for (int k=1;k<tt;k++) //第pos个厨师的倒数第wh道菜 与汇点和所有菜连边
{
for (int i=1;i<=n;i++)
add(i,(pos-1)*tt+wh+n,1,wh*a[i][pos]);
add((pos-1)*tt+wh+n,t,1,0);
ans+=doit();
}
return ans;
}
int main()
{
tt=0;
memset(st,-1,sizeof(st));
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d",&C[i]),tt+=C[i];
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
printf("%lld",solve());
return 0;
}