【BZOJ1061】【codevs1803】志愿者招募,神奇建图费用流

来源:互联网 发布:mac webpack 安装 编辑:程序博客网 时间:2024/05/05 16:16

传送门1
传送门2
写在前面:第一次写成功的费用流是个神奇数学建模题……
思路:摘自http://www.ithao123.cn/content-4207689.html,感觉这个要比列不等式+松弛操作的说法更加明白简单

设每个时间i都需要有至少Ai个志愿者,设每种志愿者i使用了xi个,那么我们对于每个时间点都可以列出一个不等式:x1+x2+x3+…+xn>=Ai(其中如果第i类志愿者不能在该区间工作则xi固定为0)。

最后要求最小化w1*x1+w2*x2+x3*x3+…+wn*xn(其中wi是第i种志愿者的单位价格)。

这正是一个线性规划的问题。

那么我们是否可以转化为网络流来求解呢?

当然是可以哒~

现在我们将每个点的Ai取反,变成-Ai,这样他就成了一个“坑”了(相对于y=0而言),我们从源点S给最左边的时间点引流,流的大小为U(U为大整数),然后每个时间点i向时间点i+1建边(i,i+1,U-Ai,0),最后设汇点为最右端的时间点。

现在,如果所有时间点的Ai都为0,那么显然,汇点的流量恰好等于U。

问题来了,现在我们有的Ai不等于0了,那么显然源点到汇点的流量被这些“坑”所截断了,怎么解决这一个问题?

假设志愿者工作的时间为【Li,Ri】,且该种志愿者单位花费为Ci,则我们建边(Li,Ri+1,INF,Ci),表示我们雇佣了一些志愿者“填坑”来了,如果雇佣了xi个志愿者,则说明将该区间内的所有“坑”的深度填掉了xi(当然可能有的坑在“填坑”行动后高于y=0,那也无所谓了嘛,多多益善~)。

那么现在是不是看起来思路有一点清晰了?

于是志愿者的作用就是一个人可以填一个区间的“坑”(好厉害!),然后需要每种志愿者选择一些使得花费最小的情况下填掉所有的“坑”,就是这样~

最后就是让费用流帮我们选择志愿者的时候了~

于是我们按照上面的方法跑完一遍最小费用最大流,如果流量等于U,则说明满流(志愿者们成功填掉了所有的坑,同志们辛苦了~),此时的记录的cost就是最小值(cost为算法记录的最小费用)。如果流量不等于U则无解。

这时我们又收获了一种费用流的模型:初始给一道大流,然后将有至少覆盖次数限制的点(边)的权值取反变成“坑”,最后区间覆盖就等于“填坑”,只要最后的流量等于大流的流量,就有解。

注意:
1.建图不要想当然,一定要遵从流量守恒等原则,最好在纸上画画写写,并手动跑一遍
2.小心int溢出,这个好坑的
代码:

#include"bits/stdc++.h"#define Maxn 0x7fffffff#define LL long longusing namespace std;int tot=1,s,t,n,m,up[20010];bool flag[20010];LL ans,dis[20010];int person[1010],start[10010],end[10010],c[10010],first[20010];struct os{    int fa,son,next;    LL limit,cost;}a[100000];queue<int> q;void add(LL x,LL y,LL z,LL co){    a[++tot].fa=x;    a[tot].son=y;    a[tot].limit=z;    a[tot].cost=co;    a[tot].next=first[x];    first[x]=tot;}bool spfa(){    memset(dis,127,sizeof(dis));    memset(up,0,sizeof(up));    dis[s]=0;    flag[s]=1;    q.push(s);    LL k;    while (!q.empty())    {        k=q.front();        for (LL i=first[k];i;i=a[i].next)        if (a[i].limit&&dis[a[i].son]>dis[k]+a[i].cost)        {            dis[a[i].son]=dis[k]+a[i].cost;            up[a[i].son]=i;            if (!flag[a[i].son]) flag[a[i].son]=1,q.push(a[i].son);        }        q.pop();        flag[k]=0;    }    if (dis[t]<0x7fffff) return 1;    else return 0;}LL flow(){    LL ans=0,minn=0x7fffffff;    for (int i=up[t];i;i=up[a[i].fa])    minn=min(minn,a[i].limit);    if (minn==Maxn) return 0;    for (int i=up[t];i;i=up[a[i].fa])    ans+=minn*a[i].cost,    a[i].limit-=minn,    a[i^1].limit+=minn;    return ans;}main(){    scanf("%d%d",&n,&m);    for (int i=1;i<=n;i++)    scanf("%d",&person[i]);    for (int i=1;i<=m;i++)    scanf("%d%d%d",&start[i],&end[i],&c[i]);    s=n+2;    t=n+1;    add(s,1,Maxn,0);    add(1,s,0,0);    for (int i=1;i<=n;i++)    add(i,i+1,Maxn-person[i],0),    add(i+1,i,Maxn,0);    for (int i=1;i<=m;i++)    add(start[i],end[i]+1,Maxn,c[i]),    add(end[i]+1,start[i],0,-c[i]);    while (spfa())     ans+=flow();    printf("%lld",ans);}
0 0
原创粉丝点击