费用流

好题

推荐这篇博客click here

注意此题如果不用动态开点的话10000的数组是不够的的,会RE

因此网络流推荐动态开点更方便(原来都是手算的)

#include<bits/stdc++.h>
using namespace std;

#define go(i,a,b) for(int i=a;i<=b;++i)
#define com(i,a,b) for(int i=a;i>=b;--i)
#define mem(a,b) memset(a,b,sizeof(a))
#define add_edge(u,v,w,co) add(u,v,w,co),add(v,u,0,-co)

const int N=110,s=0,t=10000,inf=0x3f3f3f3f;

int cnt,head[N*N],g[N][N],pre[N*N],incf[N*N],ans=0,cook[N*N],dfn[N*N],tot=0;
int d[N*N],n,m,a[N];
bool inq[N*N];
struct edge{
    int nxt,v,w,co;
}e[2*N*N*N];
queue<int>q;

void add(int u,int v,int w,int co){
    e[cnt]=(edge){head[u],v,w,co};
    head[u]=cnt++;
}

void read(int &x){
    x=0;char c=getchar(),f=1;
    while(!isdigit(c)){ if(c=='-') f=-1; c=getchar(); }
    while(isdigit(c)){ x=x*10+c-'0'; c=getchar(); }
    x*=f;
}

bool spfa(){
    mem(d,0x3f);mem(inq,0);
    q.push(s);d[s]=0;incf[s]=inf;
    while(!q.empty()){
        int u=q.front();q.pop();inq[u]=0;
        for(int i=head[u];i+1;i=e[i].nxt){
            if(!e[i].w) continue;
            int v=e[i].v,co=e[i].co;
            if(d[v]>d[u]+co){
                d[v]=d[u]+co;
                incf[v]=min(incf[u],e[i].w);
                pre[v]=i;
                if(!inq[v]) q.push(v),inq[v]=1;
            }
        }
    }
    return d[t]!=inf;
}

void EK(){
    int x=t;
    while(x!=s){
        int i=pre[x];
        e[i].w-=incf[t];
        e[i^1].w+=incf[t];
        x=e[i^1].v;
    }
    ans+=incf[t]*d[t];
    x=e[pre[t]^1].v;
    cook[++tot]=cook[x],dfn[tot]=dfn[x]+1;
    add_edge(tot,t,1,0);
    go(i,1,n) add_edge(i,tot,1,g[i][cook[tot]]*(dfn[tot]+1));
}

signed main(){
    //freopen("input.txt","r",stdin);
    mem(head,-1);
    read(n),read(m);
    go(i,1,n) read(a[i]);
    go(i,1,n)
        go(j,1,m) read(g[i][j]);
    go(i,1,n) add_edge(s,i,a[i],0);
    tot=n;
    go(i,1,m){
        cook[++tot]=i,dfn[tot]=0;
        add_edge(tot,t,1,0);
        go(j,1,n) add_edge(j,tot,1,g[j][i]);
    }
    while(spfa())
        EK();
    printf("%d",ans);
    return 0;
}
01-15 13:33