Description
暑假里,总有某些同学由于贪玩而忘记做作业。这些人往往要等到暑假快结束时才想起堆积如山的作业,但在这最后几天的时间里把这些作业做完已经不太现实了,于是“志同道合”的他们想出了一个妙招。
假设现在有n科作业,他们把第i科作业按作业量平均分成ai份,他们总共有m个人,第j个人只愿意做其中任意的bj份作业,而且我们知道ai的和等于bj的和,以及把第i科作业的其中一份给第j个人做的时间是ci,j。现在他们想分配下各自的任务,一起把作业做完,然后再%#^&%^&#%%&^
现在的问题来了,他们希望所有人做作业的总时间的和最小,你能帮助他们解决问题吗?
Input
输入文件的第一行有两个n,m表示有多少科作业和多少个人,第二行有n个数依次表示ai,第三行有m个数依次表示bj,最后n行,每行m个数表示ci,j。
Output
输出文件包含一行为最少的时间总和。
Sample Input
2 2
3 5
5 3
1 2
2 1
Sample Output
10
【样例解释】
第1个人做完所有的第1科作业以及第2科作业的其中2份,第2个人把第2科另外3份做完。
Data Constraint
第一个点 n<=5 m<=5 ai,bi<=15 sum(ai)<=20
第二个点 n<=10 m<=10 ai,bi<=20 sum(ai)<=100
第三个点 n<=30 m<=30 ai,bi<=600 sum(ai)<=10000
第四个点到第十个点 n<=200 m<=200 ai,bi<=10000 sum(ai)<=1000000
思路
显然用最小费用最大流啦!
首先,原点向每一个人连一条流量为b[i],权值为0的边,表示每一个人愿意做多少作业。
然后,每一项作业向汇点连一条流量为a[i],权值为0的边,表示总共有多少作业。
最后,每个人与作业连流量为inf,权值为c[i][j]的边,表示每个人做作业所用时间。
然后,你会TLE!!
为什么呢?想想看,我们总共练了2n^2+2n条边(加上反向边),费用流复杂度=m^2,肯定会TLE啊!
于是dalao说要用动态开点。
大致思路就是:一开始只加一部分边跑EK,如果一些边满流(根据题目确定),就加进新边,继续跑EK,如此循环往复。对于这道题,我们用数组E[i]存下与左边第i个点与右边所有的点所连的边,对每个E[i]按照花费从小到大排序。先把左边每个点i的E[i]中花费最小的边加进图,然后跑EK。记E[i][j].to表示i的第j条边所连接的右边的点,E[i][j].dist表示i的第j条边的花费,cnt[i]表示i上一次加进去的是第几条边。接着检查左边每个点i,如果E[i][cnt[i]].to连向T的边满流时,我们必须添加新边,不然有可能无法增广。于是就从cnt[i]开始从小到大加边,一直到E[i][cnt[i]].to连向T的边没有满流或者已经全部加完为止。加完以后继续跑EK。dalao说的
so,这改了我一个晚上。。。
代码
#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=10077,inf=0x3f3f3f3f;
int n,m,cnt=1,last[maxn],t,f[maxn],d[maxn],s,a[510],b[510],k[maxn],ss[maxn],ans,dis[maxn],w[maxn],mx;
struct C
{
int d,v;
}c[510][510];
struct E
{
int to,next,flow,cost;
}e[maxn*30];
bool cmp(C x,C y)
{
return x.v<y.v;
}
void add(int u,int v,int x,int y)
{
e[++cnt].to=v; e[cnt].flow=x; e[cnt].cost=y; e[cnt].next=last[u]; last[u]=cnt;
e[++cnt].to=u; e[cnt].flow=0; e[cnt].cost=-y; e[cnt].next=last[v]; last[v]=cnt;
}
bool spfa()
{
queue<int> q;
for (int i=s;i<=t;i++)
{
dis[i]=inf;
f[i]=d[i]=0;
}
q.push(s);
f[s]=1; dis[s]=0; w[s]=inf;
while (!q.empty())
{
int u=q.front(); q.pop();
for(int i=last[u]; i; i=e[i].next)
if(e[i].flow&&e[i].cost+dis[u]<dis[e[i].to])
{
dis[e[i].to]=e[i].cost+dis[u];
w[e[i].to]=min(w[u],e[i].flow);
d[e[i].to]=i;
if (!f[e[i].to])
{
f[e[i].to]=1;
q.push(e[i].to);
}
}
f[u]=0;
}
if (dis[t]<inf) return 1; else return 0;
}
void mcmf()
{
int x=t;
while (x!=s)
{
int i=d[x];
e[i].flow-=w[t];
e[i^1].flow+=w[t];
x=e[i^1].to;
}
mx+=w[t];
ans+=dis[t]*w[t];
}
void solve()
{
while (spfa())
{
mcmf();
for(int i=1; i<=n; i++)
while (!e[k[c[i][ss[i]].d]].flow&&ss[i]<m)
{
ss[i]++;
add(i,n+c[i][ss[i]].d,a[i],c[i][ss[i]].v);
//,printf("add %d to %d\n",i,c[i][ss[i]].d+n);
}
}
}
int main()
{
scanf("%d%d",&n,&m);
s=0; t=m+n+1;
for(int i=1; i<=n; i++) scanf("%d",&a[i]);
for(int i=1; i<=m; i++) scanf("%d",&b[i]);
for(int i=1; i<=n; i++)
{
for (int j=1; j<=m; j++) scanf("%d",&c[i][j].v),c[i][j].d=j;
sort(c[i]+1,c[i]+m+1,cmp);
ss[i]=1;
add(i,n+c[i][1].d,a[i],c[i][1].v);
}
for(int i=1; i<=n; i++) add(s,i,a[i],0);
for(int i=1; i<=m; i++) add(n+i,t,b[i],0),k[i]=cnt-1;
solve();
printf("%d",ans);
return 0;
}