[费用流]作业分配

题目描述

暑假里,总有某些同学由于贪玩而忘记做作业。这些人往往要等到暑假快结束时才想起堆积如山的作业,但在这最后几天的时间里把这些作业做完已经不太现实了,于是“志同道合”的他们想出了一个妙招。
假设现在有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

分析

容易看出费用流,但是时限500ms跑不过去,加了一个玄学优化:动态加边可以优化1500ms,从而AC

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <memory.h>
#define rep(i,a,b) for (i=a;i<=b;i++)
const int N=501;
const int oo=1061109567;
using namespace std;
struct Edge {
    int u,v,c,w,nx;
}g[2*N*N];
struct Edge_notadd {
    int v,c;
}e[N][N];
int ecnt[N],pt[N];
int cnt=1,list[N];
int n,m,s,t;
int d[N],f[N];
int ans;

bool Cmp(Edge_notadd a,Edge_notadd b) {
    return a.c<b.c;
}

void Add(int u,int v,int c,int w) {
    g[++cnt].u=u;g[cnt].v=v;g[cnt].c=c;g[cnt].w=w;g[cnt].nx=list[u];list[u]=cnt;
    g[++cnt].u=v;g[cnt].v=u;g[cnt].c=0;g[cnt].w=-w;g[cnt].nx=list[v];list[v]=cnt;
}

bool Spfa() {
    queue<int> q;
    bool b[N];
    while (!q.empty()) q.pop();
    q.push(s);
    memset(d,0x3f,sizeof d);memset(b,0,sizeof b);memset(f,0,sizeof f);
    d[s]=0;b[s]=1;
    while (!q.empty()) {
        int u=q.front();q.pop();
        for (int i=list[u];i;i=g[i].nx)
        if (g[i].c&&d[g[i].v]>d[u]+g[i].w) {
            d[g[i].v]=d[u]+g[i].w;
            f[g[i].v]=i;
            if (!b[g[i].v])
            q.push(g[i].v);
            b[g[i].v]=1;
        }
        b[u]=0;
    }
    return d[t]<oo;
}

void Mcf() {
    int x=t,cost=d[t],mf=oo;
    while (f[x]) {
        x=f[x];
        mf=min(mf,g[x].c);
        x=g[x].u;
    }
    ans+=cost*mf;x=t;
    while (f[x]) {
        x=f[x];
        g[x].c-=mf;
        g[x^1].c+=mf;
        x=g[x].u;
    }
    int i;
    rep(i,1,n)
    while (!g[pt[e[i][ecnt[i]].v]].c&&ecnt[i]<m) {
        ecnt[i]++;
        Add(i,e[i][ecnt[i]].v,oo,e[i][ecnt[i]].c);
    }
}

void Dinic() {
    while (Spfa())
    Mcf();
}

int main() {
    int i,j;
    scanf("%d%d",&n,&m);
    s=n+m+1;t=n+m+2;
    rep(i,1,n) {
        int w;scanf("%d",&w);
        Add(s,i,w,0);
    }
    rep(i,1,m) {
        int w;scanf("%d",&w);
        Add(n+i,t,w,0);
        pt[n+i]=cnt-1;
    }
    rep(i,1,n)
    {
        rep(j,1,m)
        {
            int w;scanf("%d",&w);
            e[i][j].v=n+j;
            e[i][j].c=w;
        }
        sort(e[i]+1,e[i]+m+1,Cmp);
        Add(i,e[i][1].v,oo,e[i][1].c);
        ecnt[i]=1;
    }
    Dinic();
    printf("%d",ans);
}

猜你喜欢

转载自blog.csdn.net/ssl_qyh0ice/article/details/81040241