[BZOJ]4842: [Neerc2016]Delight for a Cat 1061: [Noi2008]志愿者招募 费用流 线性规划

题解:

把这两道题目放在一起写,因为做法是一样的。
两题都是经典的线性规划问题,但同时也都可以用费用流解决。
以志愿者招募这题为例,首先设第 i i i类志愿者有 x i x_i xi人。
那么根据样例可以列出不等式:
{ x 1 ≥ 2 x 1 + x 2 ≥ 3 x 2 + x 3 ≥ 4 \begin{cases} x_1\geq2\\ x_1+x_2\geq3\\ x_2+x_3\geq4 \end{cases} x12x1+x23x2+x34
我们的目标是使 2 x 1 + 5 x 2 + 2 x 3 2x_1+5x_2+2x_3 2x1+5x2+2x3最小。
把不等式改写成等式:
{ x 1 = 2 + y 1 x 1 + x 2 = 3 + y 2 x 2 + x 3 = 4 + y 3 \begin{cases} x_1=2+y_1\\ x_1+x_2=3+y_2\\ x_2+x_3=4+y_3 \end{cases} x1=2+y1x1+x2=3+y2x2+x3=4+y3
然后再差分一下,同时保留第一个原式和最后一个不等式的符号取反,得:
{ x 1 = y 1 + 2 x 2 = − y 1 + y 2 + 1 − x 1 + x 3 = − y 2 + y 3 + 1 − x 2 − x 3 = − y 3 − 4 \begin{cases} x_1=y_1+2\\ x_2=-y_1+y_2+1\\ -x_1+x_3=-y_2+y_3+1\\ -x_2-x_3=-y_3-4 \end{cases} x1=y1+2x2=y1+y2+1x1+x3=y2+y3+1x2x3=y34
再让全部符号变成正号,移项得:
{ x 1 = y 1 + 2 x 2 + y 1 = y 2 + 1 x 3 + y 2 = x 1 + y 3 + 1 y 3 + 4 = x 2 + x 3 \begin{cases} x_1=y_1+2\\ x_2+y_1=y_2+1\\ x_3+y_2=x_1+y_3+1\\ y_3+4=x_2+x_3 \end{cases} x1=y1+2x2+y1=y2+1x3+y2=x1+y3+1y3+4=x2+x3
然后可以发现每个未知量都出现且仅出现了两次(这个由题目的性质决定),而且一次是在等号左边一次在右边,然后可以开始建图了,把每个等式看做是一个点,右边看做是流入量,左边看做是流出量,由于满足流量平衡,所以满足等式。因此流量的含义就是每个数的大小,如果是未知数,那么在它左边出现的式子向右边出现的式子连一条流量为 i n f inf inf,费用为 C i C_i Ci的边,含义是 x i x_i xi可以取无限大,每取一个有代价。如果是常数,就由源点补充或流向汇点。
跑费用流就可以了。

4842代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=1010;
const LL inf=(1LL<<50);
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
LL ans=0;
int lx[Maxn],rx[Maxn],ly[Maxn],ry[Maxn],lz[Maxn],rz[Maxn];
int n,k,t1,t2,si[Maxn],ei[Maxn],st,ed;//t1 s t2 e
struct Edge{int x,y,next;LL d,c;}e[Maxn<<4];
int last[Maxn<<3],len=1;
void ins(int x,int y,LL d,LL c)
{
    int t=++len;
    e[t].x=x;e[t].y=y;e[t].d=d;e[t].c=c;
    e[t].next=last[x];last[x]=t;
}
void addedge(int x,int y,LL d,LL c){ins(x,y,d,c);ins(y,x,0LL,-c);}
LL f[Maxn<<3];int pre[Maxn<<3];bool in[Maxn<<3];
bool spfa()
{
    pre[st]=-1;
    memset(in,false,sizeof(in));
    memset(f,-63,sizeof(f));f[st]=0;
    queue<int>q;q.push(st);
    while(!q.empty())
    {
        int x=q.front();q.pop();in[x]=false;
        for(int i=last[x];i;i=e[i].next)
        {
            int y=e[i].y;
            if(e[i].d&&f[x]+e[i].c>f[y])
            {
                f[y]=f[x]+e[i].c;pre[y]=i;
                if(!in[y])in[y]=true,q.push(y);
            }
        }
    }
    return f[ed]>-inf;
}
void work()
{
    int x=ed;LL mn=inf;
    while(pre[x]!=-1)
    {
        mn=min(mn,e[pre[x]].d);
        x=e[pre[x]].x;
    }
    x=ed;
    while(pre[x]!=-1)
    {
        e[pre[x]].d-=mn;e[pre[x]^1].d+=mn;
        ans+=e[pre[x]].c*mn;
        x=e[pre[x]].x;
    }
}
int main()
{
    n=read(),k=read(),t1=read(),t2=read();
    st=(n-k+1)*2+2,ed=st+1;
    for(int i=1;i<=n;i++)si[i]=read();
    for(int i=1;i<=n;i++)ei[i]=read(),ans+=(LL)ei[i];
    for(int i=1;i<=k;i++)lx[i]=1;
    for(int i=n-k+1;i<=n;i++)rx[i]=(n-k+1)*2+1;
    for(int i=1;i<=n-k;i++)lx[i+k]=rx[i]=(i<<1)+1;
    for(int i=1;i<=n-k+1;i++)ly[i]=i<<1,ry[i]=(i<<1)-1;
    for(int i=1;i<=n-k+1;i++)lz[i]=i<<1,rz[i]=(i<<1)+1;
    for(int i=1;i<=n;i++)addedge(lx[i],rx[i],1,si[i]-ei[i]);
    for(int i=1;i<=n-k+1;i++)addedge(ly[i],ry[i],inf,0LL);
    for(int i=1;i<=n-k+1;i++)addedge(lz[i],rz[i],inf,0LL);
    addedge(st,1,t1,0LL);
    for(int i=2;i<(n-k+1)*2+1;i++)
    if(i&1)addedge(i,ed,k-t1-t2,0LL);
    else addedge(st,i,k-t1-t2,0LL);
    addedge((n-k+1)*2+1,ed,k-t2,0LL);
    while(spfa())work();
    printf("%lld\n",ans);
    for(int i=1;i<=n;i++)
    if(e[i<<1].d)putchar('E');
    else putchar('S');
}

1061代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=1010;
const int Maxm=10010;
const LL inf=(1LL<<50);
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int n,m,st,ed;
LL a[Maxn],S[Maxm],T[Maxm],C[Maxm],ans=0;
struct Edge{int x,y,next;LL d,c;}e[Maxm<<2];
int last[Maxn<<1],len=1;
void ins(int x,int y,LL d,LL c)
{
    int t=++len;
    e[t].x=x;e[t].y=y;e[t].d=d;e[t].c=c;
    e[t].next=last[x];last[x]=t;
}
void addedge(int x,int y,LL d,LL c){ins(x,y,d,c);ins(y,x,0LL,-c);}
LL f[Maxn<<1];int pre[Maxn<<1];bool in[Maxn<<1];
bool spfa()
{
    pre[st]=-1;
    memset(in,false,sizeof(in));
    memset(f,63,sizeof(f));f[st]=0;
    queue<int>q;q.push(st);
    while(!q.empty())
    {
        int x=q.front();q.pop();in[x]=false;
        for(int i=last[x];i;i=e[i].next)
        {
            int y=e[i].y;
            if(e[i].d&&f[x]+e[i].c<f[y])
            {
                f[y]=f[x]+e[i].c;pre[y]=i;
                if(!in[y])in[y]=true,q.push(y);
            }
        }
    }
    return f[ed]<inf;
}
void work()
{
    int x=ed;LL mn=inf;
    while(pre[x]!=-1)
    {
        mn=min(mn,e[pre[x]].d);
        x=e[pre[x]].x;
    }
    x=ed;
    while(pre[x]!=-1)
    {
        e[pre[x]].d-=mn;e[pre[x]^1].d+=mn;
        ans+=e[pre[x]].c*mn;
        x=e[pre[x]].x;
    }
}
int main()
{
    n=read(),m=read();st=n+2,ed=n+3;
    for(int i=1;i<=n;i++)a[i]=read(),addedge(i+1,i,inf,0);a[0]=a[n+1]=0;
    for(int i=1;i<=m;i++)S[i]=read(),T[i]=read(),C[i]=read(),addedge(S[i],T[i]+1,inf,C[i]);
    for(int i=1;i<=n+1;i++)
    {
        if(a[i]>a[i-1])addedge(st,i,a[i]-a[i-1],0);
        else addedge(i,ed,a[i-1]-a[i],0);
    }
    while(spfa())work();
    printf("%lld",ans);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值