码迷,mamicode.com
首页 > 其他好文 > 详细

[网络流24题] 餐巾计划问题 [费用流]

时间:2018-01-31 00:57:12      阅读:218      评论:0      收藏:0      [点我收藏+]

标签:需求   传送门   for   long   void   col   efi   main   cout   

题面:

https://www.luogu.org/problemnew/show/P1251

思路:

这道题乍一看,可以跑上下界费用流

代码量、难度 -> inf

其实不然,我们可以用费用流的特殊处理去掉下界

观察题目,每天要求有ri块餐巾

首先,有贪心如下:

当且仅当每天可供使用的餐巾正好满足需求时,可以有最小费用

证明:若某一天有多一块餐巾,则其根本来源一定是买多了,而且在这块餐巾参与的周转中还消费了一些清洗费用,同时它造成其余的日子里也会有餐巾被闲置

因此首先把题目转化为“每天正好使用ri”

此时考虑拆点,将每一天拆成xi,yi,其中yi连边到T,容量为ri,费用为0,表示该天需要这么多餐巾。(以下这类边表示为 (cap,cost) )

然后开始建图,思路是保证yi到T的边可以满流的情况下,构建费用等同于题目要求的网络。

首先定义xi为“第i天对于用完的餐巾做出的决策”,因此可以从xi到xi+1连一条边 (inf,0),其边上的流量表示“第i+1天保留第i天中(流量)块餐巾的决策”

然后从S到xi连边 (ri,0) ,代表该天需要做出这一决策的餐巾数量,也就是该天使用了的餐巾数量

从S到yi连边(inf,p)代表新买一块。这块餐巾不需要参与xi的“旧餐巾决策”,直接连到yi

从xi到yi+m、yi+n分别连边 (inf,f) (inf,s) ,代表快洗和慢洗决策。

然后在这张图上跑S-T最小费用最大流即可

 

Code:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define inf 100000000000000LL
#define ll long long
using namespace std;
inline ll read(){
    ll re=0,flag=1;char ch=getchar();
    while(ch>9||ch<0){
        if(ch==-) flag=-1;
        ch=getchar();
    }
    while(ch>=0&&ch<=9) re=(re<<1)+(re<<3)+ch-0,ch=getchar();
    return re;
}
ll n,m,cnt,ans,flow,first[100010];
ll dis[100010],pre[100010],path[100010];
struct edge{
    ll to,next,w,cap;
}a[100010];
inline void add(ll u,ll v,ll w,ll cap){
//    cout<<"add "<<u<<ends<<v<<ends<<w<<ends<<cap<<endl;
    a[++cnt]=(edge){v,first[u],w,cap};first[u]=cnt;
    a[++cnt]=(edge){u,first[v],-w,0};first[v]=cnt;
}
void init(){
    memset(first,-1,sizeof(first));
    memset(a,0,sizeof(a));cnt=-1;ans=0;flow=0;
}
ll spfa(ll s,ll t){
    ll q[10010],head=0,tail=1,maxn=10000,i,u,v,w;
    memset(pre,-1,sizeof(pre));
    for(i=s;i<=t;i++) dis[i]=inf;
    q[0]=s;dis[s]=0;
    while(head!=tail){
        u=q[head];head=(head+1)%maxn;
//        cout<<"spfa "<<u<<endl;
        for(i=first[u];~i;i=a[i].next){
            v=a[i].to;w=a[i].w;
//            cout<<"    to "<<v<<ends<<dis[v]<<ends<<w+dis[u]<<endl;
            if(a[i].cap&&dis[v]>dis[u]+w){
                dis[v]=dis[u]+w;
                pre[v]=u;path[v]=i;
                q[tail]=v;tail=(tail+1)%maxn;
            }
        }
    }
    return ~pre[t];
}
inline ll _min(ll l,ll r){return (l<r)?l:r;}
ll mcmf(ll s,ll t){
    ll f,u;
    while(spfa(s,t)){
        f=inf;
        for(u=t;u!=s;u=pre[u]) f=_min(f,a[path[u]].cap);
        flow+=f;ans+=dis[t]*f;
//        cout<<"mcmf "<<flow<<ends<<ans<<endl;
        for(u=t;u!=s;u=pre[u]){
            a[path[u]].cap-=f;
            a[path[u]^1].cap+=f;
        }
    }
}
int main(){
//    freopen("napkin.in","r",stdin);
//    freopen("napkin.out","w",stdout);
    init();
    ll i,t1,t2;
    n=read();
    for(i=1;i<=n;i++){
        t1=read();
        add(0,i,0,t1);
        add(i+n,(n<<1)+1,0,t1);
    }
    for(i=1;i<n;i++) add(i,i+1,0,inf);
    t1=read();
    for(i=1;i<=n;i++) add(0,i+n,t1,inf);
    t1=read();t2=read();
    for(i=1;i<=n-t1;i++) add(i,i+t1+n,t2,inf);
    t1=read();t2=read();
    for(i=1;i<=n-t1;i++) add(i,i+t1+n,t2,inf);
    mcmf(0,(n<<1)+1);
    printf("%lld",ans);
}

 

[网络流24题] 餐巾计划问题 [费用流]

标签:需求   传送门   for   long   void   col   efi   main   cout   

原文地址:https://www.cnblogs.com/dedicatus545/p/8387692.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!