【网络流24题之餐巾计划】xoj 1247

网络流 xoj 1247: 餐巾计划问题 题解

根据题意,不难发现题目要求求出最小费用流,这题难在建模。
首先我们找到限制条件,也就是每天需要的餐巾数量。每天的餐巾既有进又有出(即旧餐巾)。不妨将旧餐巾看做从源点(建一个源点),到1~N点的流量,费用为0,表示每天获得的旧餐巾,需要处理。然后再多建(N+1)~(N+N)这N个点,与汇点(建一个汇点)的连边为每天需要的餐巾数。
再根据题目给出的三种获得新餐巾的方式(一种直接购买;两种处理旧餐巾,分别是快洗和慢洗)。处理旧餐巾可以连1~N到(N+1)~(N+N)的边,表示(N+1)~(N+N)这些天的新餐巾可以通过1~N中某(几)天的旧餐巾经过处理后得到的。连边即 (i,i+N+tf,inf,pf)(i,i+N+ts,inf,ps) 。tf是快洗的时间,pf 是快洗的费用;ts,ps分别是慢洗的时间和费用。边:(起点,终点,流量,费用)。
不要漏了,每天可以将旧餐巾留到后一天,所以多建 (i,i+1,inf,0) 。然后就是购买的餐巾直接连接源点与(N+1)~(N+N),费用为p,流量为inf。
具体代码如下

#include <iostream>
#include <cstdio>
#include <queue>
#define _(d) while(d (((ch=getchar())>47)&&ch<58))
using namespace std;
const int MaxN=1005<<2,Maxe=1005*1005;
const int inf=0x7fffffff;
int N,p,pf,ps,tf,ts;//days,fee of buy,fee of fast/slow ones,times of fast/slow ones;
int ans,h[MaxN],d[MaxN],pre[MaxN];
bool vst[MaxN];
int S,T,tt,from[Maxe],to[Maxe],flow[Maxe],cst[Maxe],nxt[Maxe];//edge
inline void Get(int &x){char ch;_(!);x=ch-48;_()x=(x<<3)+(x<<1)+ch-48;}
inline void addedge(int u,int v,int w,int cc)
{
    from[tt]=u;to[tt]=v;flow[tt]=w;cst[tt]=cc;
    nxt[tt]=h[u];h[u]=tt++; 
}
inline void add(int u,int v,int w,int cc)
{
    addedge(u,v,w,cc);addedge(v,u,0,-cc);   
}
queue<int> q;
int spfa()
{
    for(int i=0;i<MaxN;i++) vst[i]=0,d[i]=inf,pre[i]=-1;
    q.push(S);
    vst[S]=1;d[S]=0;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        vst[u]=0;
        for(int i=h[u];i!=-1;i=nxt[i])
        {
            int v=to[i];
            if(flow[i]>0&&d[v]>d[u]+cst[i])
            {
                d[v]=d[u]+cst[i];
                pre[v]=i;
                if(!vst[v])
                {
                    vst[v]=1;
                    q.push(v);  
                }   
            }   
        }   
    }
    return d[T]<inf;
}
void work()
{
    while(spfa())
    {
        int ft=inf;
        for(int i=T;i!=S;i=from[pre[i]]){int k=pre[i];ft=min(ft,flow[k]);}
        for(int i=T;i!=S;i=from[pre[i]])
        {
            int k=pre[i];
            ans+=cst[k]*ft;
            flow[k]-=ft;
            flow[k^1]+=ft;  
        }
    }
    return ;
}
int main()
{
    freopen("1247.in","r",stdin);
    for(int i=0;i<MaxN;i++) h[i]=-1;
    int t1;
    Get(N);Get(p);Get(tf);Get(pf);Get(ts);Get(ps);
    T=2001;
    for(int i=1;i<=N;i++)
    {
        Get(t1);
        add(S,i,t1,0);
        add(i+N,T,t1,0);
        if(i+1<=N)  add(i,i+1,inf,0);//将旧餐巾留到第二天洗 
        if(i+N+tf<=2*N) add(i,i+N+tf,inf,pf);//将旧餐巾给快洗 
        if(i+N+ts<=2*N) add(i,i+N+ts,inf,ps);//将旧餐巾给慢洗 
        add(S,i+N,inf,p);

    }
    work();
    printf("%d\n",ans);
    return 0;   
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值