P1251 餐巾计划问题

题面

一道特别棒的费用流好题,思路非常巧妙

对于每一天,我们分为早上和晚上两个节点

每天早上会消耗\(r_i\)条干净的毛巾,可以视为流向汇点,所以我们向汇点连一条流量为\(r_i\),费用为\(0​\)的边

每天晚上会产生\(r_i\)条脏毛巾,可以视为从源点流出,所以我们从源点连一条流量为\(r_i\),费用为\(0\)的边

然后我们考虑以下四种情况:买毛巾,慢洗,快洗,留着攒味

首先是买毛巾,我们直接让毛巾从源点流向每个早上,所以从源点连一条流量无限大,费用为\(p\)的边

然后是慢洗,我们从每个晚上向\(n\)天之后的早上连一条流量无限大,费用为\(s\)的边,快洗处理方式类似

最后是保存,我们直接从每个晚上向下一个晚上连一条流量无限大,费用为\(0\)的边即可

以上所建的所有边都需要建对应的反边,这也是费用流的常规写法

图建好之后,这道题可以说已经做完了,写模板就是了

下面放代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cctype>
#include<queue>
#define ll long long
#define gc getchar
#define maxn 4005
#define maxm 100005
using namespace std;

inline ll read(){
    ll a=0;int f=0;char p=gc();
    while(!isdigit(p)){f|=p=='-';p=gc();}
    while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=gc();}
    return f?-a:a;
}int n,S,T,a,b,c,g,f;ll ans;

struct ahaha{
    int f,w,to,next;
}e[maxm<<1];int tot,head[maxn];
inline void add(int u,int v,int w,int f){
    e[tot]={f,w,v,head[u]};head[u]=tot++;
}

deque<int>q;
int bi[maxn],d[maxn],fl[maxn],la[maxn];
int spfa(){
    memset(fl,63,sizeof fl);memset(d,63,sizeof d);
    memset(bi,0,sizeof bi);d[S]=0;la[T]=-1;q.push_back(S);
    while(!q.empty()){
        int u=q.front();q.pop_front();bi[u]=0;
        for(int i=head[u];~i;i=e[i].next){
            int v=e[i].to;
            if(d[v]<=d[u]+e[i].f||e[i].w<=0)continue;
            d[v]=d[u]+e[i].f;la[v]=i;
            fl[v]=min(fl[u],e[i].w);
            if(bi[v])continue;bi[v]=1;
            if(q.empty()||d[v]<d[q.front()])q.push_front(v);
            else q.push_back(v);
        }
    }return ~la[T];
}

int main(){memset(head,-1,sizeof head);
    n=read();S=0;T=n<<1|1;
    for(int i=1;i<=n;++i){
        int w=read();
        add(S,i+n,w,0);add(i+n,S,0,0);
        add(i,T,w,0);add(T,i,0,0);
    }
    for(int i=1;i<n;++i){
        add(i+n,i+n+1,2147483647,0);
        add(i+n+1,i+n,0,0);
    }
    a=read();b=read();c=read();g=read();f=read();
    for(int i=1;i<=n;++i){
        add(S,i,2147483647,a);
        add(i,S,0,-a);
    }
    for(int i=n-b;i;--i){
        add(i+n,i+b,2147483647,c);
        add(i+b,i+n,0,-c);
    }
    for(int i=n-g;i;--i){
        add(i+n,i+g,2147483647,f);
        add(i+g,i+n,0,-f);
    }
    while(spfa()){
        ans+=(ll)fl[T]*d[T];
        int now=T;
        while(now!=S){
            e[la[now]].w-=fl[T];
            e[la[now]^1].w+=fl[T];
            now=e[la[now]^1].to;
        }
    }
    printf("%lld\n",ans);
    return 0;
}
posted @ 2019-02-25 21:42 子谦。 阅读(...) 评论(...) 编辑 收藏
Live2D
//雪