BZOJ1061 [Noi2008]志愿者招募(洛谷P3980/BZOJ3265)

版权声明:蒟蒻Blog随意转载 https://blog.csdn.net/a1799342217/article/details/81562163

费用流

BZOJ1061
BZOJ3265
洛谷P3980

单纯形裸题,然而我不会,只能费用流。
建图很妙。

源点到第一天连边,最后一天的后一天到汇点连边,容量为INF。每一天向后一天连边,容量为INF-a[i]。费用都为0。对于每一类志愿者,连 S i T i + 1 的容量为INF,费用为 C i 的边。注意最后一天之后还要建一个点(因为 T i + 1 )。此时跑出来的费用就是最小费用。

为什么这么建?我们来口胡一下

首先这道题的容量没有上界只有下界。那么我们反过来建就可以跑最大流了。
现在我们要把源点的流量补成INF,那么就要建一些额外边,也就是志愿者的边。因为每一类志愿者只要雇佣就能从 S i 干到 T i ,所以连 S i T i + 1

代码:

#include<queue>
#include<cctype>
#include<cstdio>
#include<algorithm>
#define N 1005
#define M 11005
#define F inline
using namespace std;
struct edge{ int nxt,to,d,v,f; }ed[M*100];
struct fat{ int x,e; }fa[N];
int n,m,k=1,s,t,ans,h[N],d[N],rem[N],tmp[N][2];
queue <int> q; bool f[N];
F char readc(){
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    return l==r?EOF:*l++;
}
F int _read(){
    int x=0; char ch=readc();
    while (!isdigit(ch)) ch=readc();
    while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
    return x;
}
F void addedge(int x,int y,int z,int v){
    ed[++k]=(edge){h[x],y,z,v,0},h[x]=k;
    ed[++k]=(edge){h[y],x,-z,0,0},h[y]=k;
}
F int spfa(){
    for (int i=s;i<=t;i++) f[i]=false,d[i]=1e9;
    while (!q.empty()) q.pop(); q.push(s),d[s]=0,rem[s]=1e9;
    while (!q.empty()){
        int x=q.front(); f[x]=false,q.pop();
        for (int i=h[x],v;i;i=ed[i].nxt)
            if (ed[i].v>ed[i].f&&d[x]+ed[i].d<d[v=ed[i].to]){
                d[v]=d[x]+ed[i].d,fa[v].e=i,fa[v].x=x;
                rem[v]=min(rem[x],ed[i].v-ed[i].f);
                if (!f[v]) q.push(v),f[v]=true;
            }
    }
    if (d[t]==1e9) return 0;
    return rem[t];
}
int main(){
    n=_read(),m=_read(),t=n+2;
    addedge(s,1,0,1e6),addedge(n+1,t,0,1e6);
    for (int i=1;i<=n;i++)
        addedge(i,i+1,0,1e6-_read());
    for (int i=1,p,z;i<=m;i++){
        p=_read();
        for (int j=1;j<=p;j++)
            tmp[j][0]=_read(),tmp[j][1]=_read();
        z=_read();
        for (int j=1;j<=p;j++)
            addedge(tmp[j][0],tmp[j][1]+1,z,1e9);
    }
    for (int sum;sum=spfa();ans+=d[t]*sum)
        for (int x=t,e;x!=s;x=fa[x].x)
            ed[e=fa[x].e].f+=sum,ed[e^1].f-=sum;
    return printf("%d\n",ans),0;
}

猜你喜欢

转载自blog.csdn.net/a1799342217/article/details/81562163
今日推荐