餐巾计划问题

P1251 餐巾计划问题

分析

奇妙的拆点用法。

分析完题目后,我们发现每一天需要\(r_i\)块新毛巾,并产生\(r_i\)块旧毛巾。

这不难想到,将一天拆开,分成还有的老毛巾和需要的新毛巾。

我们考虑一下,建图过程。

  • 从源点向所有老毛巾的点连接容量为 \(r_i\) 费用为0的边,再从所有的新毛巾向汇点连一条容量为\(r_i\)费用为0的边
  • 下边三条皆为老毛巾的去处,第二三条为新毛巾的来源
  • 对于左边的第\(i\)天的老毛巾,其可以向下一天的老毛巾连一条容量为无穷费用为0的边
  • 对于左边的第\(i\)天的老毛巾,其可以向第\(i+m\)天的新毛巾连一条容量为无穷费用为f的边
  • 对于左边的第\(i\)天的老毛巾,其可以向第\(i+n\)天的新毛巾连一条容量为无穷费用为s的边
  • 对应的新毛巾的最后一条来源,是从源点向每一个新毛巾的连一条容量为无穷费用为p的边

然后跑最小费用最大流即可。

这里着重说一下,就是对于,在最大流的结果下,可能从源点向老毛巾连出的所有边不一定都是满流,可以理解为当天用完后,直接有一部分我们不要了,因为可能直接买效益更高。

AC_code

#include<bits/stdc++.h>

using namespace std;
typedef long long LL;
const int N = 4010,M = N*10,INF = 1e8;

int h[N],ne[M],e[M],f[M],w[M],idx;
int q[N],d[N],minT[N],pre[N];
bool st[N];
int r[N];
int n,p,x,xp,y,yp,S,T;

void add(int a,int b,int c,int d)
{
    e[idx] = b,ne[idx] = h[a],w[idx] = d,f[idx] = c,h[a] = idx++;
    e[idx] = a,ne[idx] = h[b],w[idx] = -d,f[idx] = 0,h[b] = idx++;
}

bool spfa()
{
    int hh = 0,tt = 0;
    memset(d,0x3f,sizeof d);
    memset(minT,0,sizeof minT);
    d[S] = 0,q[tt++] = S,minT[S] = INF;
    while(hh!=tt)
    {
        int t = q[hh++];
        if(hh==N) hh = 0;
        st[t] = 0;
        for(int i=h[t];~i;i=ne[i])
        {
            int j = e[i];
            if(f[i]&&d[j]>d[t]+w[i])
            {
                d[j] = d[t] + w[i];
                minT[j] = min(f[i],minT[t]);
                pre[j] = i;
                if(!st[j])
                {
                    st[j] = 1;
                    q[tt++] = j;
                    if(tt==N) tt = 0;
                }
            }
        }
    }
    return minT[T] > 0;
}

LL EK()
{
    LL cost = 0;
    while(spfa())
    {
        int t = minT[T];
        cost += 1ll*d[T]*t;
        for(int i=T;i!=S;i=e[pre[i]^1])
            f[pre[i]] -= t,f[pre[i]^1] += t;
    }
    return cost;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",r+i);
    scanf("%d%d%d%d%d",&p,&x,&xp,&y,&yp);
    memset(h,-1,sizeof h);
    S = 0,T = 2*n + 1;
    for(int i=1;i<=n;i++)
    {
        add(S,i,r[i],0);
        add(i+n,T,r[i],0);
        add(S,i+n,INF,p);
        if(i+1<=n) add(i,i+1,INF,0);
        if(i+x<=n) add(i,i+n+x,INF,xp);
        if(i+y<=n) add(i,i+n+y,INF,yp);
    }
    printf("%lld\n",EK());
    return 0;
}
posted @ 2022-07-11 18:19  艾特玖  阅读(33)  评论(0)    收藏  举报