并不对劲的餐巾计划问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并不对劲的餐巾计划问题相关的知识,希望对你有一定的参考价值。

********************题目略长**********************

题目描述

一个餐厅在相继的N天里,第i天需要Ri块餐巾(i=l,2,…,N)。餐厅可以从三种途径获得餐巾。

(1)购买新的餐巾,每块需p分;

(2)把用过的餐巾送到快洗部,洗一块需m天,费用需f分(f<p)。如m=l时,第一天送到快洗部的餐巾第二天就可以使用了,送慢洗的情况也如此。

(3)把餐巾送到慢洗部,洗一块需n天(n>m),费用需s分(s<f)。

在每天结束时,餐厅必须决定多少块用过的餐巾送到快洗部,多少块送慢洗部。在每天开始时,餐厅必须决定是否购买新餐巾及多少,使洗好的和新购的餐巾之和满足当天的需求量Ri,并使N天总的费用最小

输入输出格式

输入格式:

输入文件共3行,第1行为总天数;第2行为每天所需的餐巾块数;第3行为每块餐巾的新购费用p,快洗所需天数m,快洗所需费用f,慢洗所需天数n,慢洗所需费用s。

输出格式:

输出文件共1行为最小的费用。

输入输出样例

输入样例:
3
3 2 4
10 1 6 2 3
输出样例:
64

说明

N<=2000

ri<=10000000

p,f,s<=10000

时限4s

*************在网络流24题中算是神奇的一题************

网络流24题中,存在各种只是来让人练写模板熟练度的题。不过这题的建图方式算是一股清流,让人很不好想,但想到了也很不好证明,不过证明出来后真的很好写。

对于送到快洗部、慢洗部、直接购买都很好想,拆点后直接连就行了。也就是把一天拆成上午和下午两个点,上午可以接受洗完的餐巾、买新餐巾,下午可以把餐巾送去洗。也就是说,上午的“流”的意义是干净的餐巾,下午的“流”的意义是脏的餐巾。

让第i天“经过”汇点的流为ni显然是行不通的。可以把“拿去用ni个餐巾”拆成两个动作:上午把ni个干净的餐巾给汇点,下午接收从源点来的ni个脏餐巾。

还要注意的是,餐厅里可以攒干净的餐巾,所以第i天上午要连一条边到第i+1天上午。

以上过程其实可以用有上下界网络流来思考,但并不对劲的人并不想这么做。

技术分享图片
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define ll long long
#define maxn 4010
#define maxm 2000010
using namespace std;
const ll inf=0x7fffffff;
ll fir[maxn],nxt[maxm],v[maxm],fl[maxm],w[maxm],cnt;
ll mincost,n,m,p,f,s,N,dis[maxn];
bool vis[maxn];
ll read()
{
    ll x=0,f=1;
    char ch=getchar();
    while(isdigit(ch)==0 && ch!=-)
        ch=getchar();
    if(ch==-)f=-1,ch=getchar();
    while(isdigit(ch))x=x*10+ch-0,
        ch=getchar();
    return x*f;
}
void addedge(ll u1,ll v1,ll fl1,ll w1)
{
    w[cnt]=w1,v[cnt]=v1,fl[cnt]=fl1,nxt[cnt]=fir[u1],fir[u1]=cnt++;
    w[cnt]=-w1,v[cnt]=u1,fl[cnt]=0,nxt[cnt]=fir[v1],fir[v1]=cnt++;
}
ll spfa() 
{
    memset(dis,-1,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[N*2+1]=0;
    deque<ll >q;
    q.push_back(N*2+1);
    while(!q.empty())
    {
        ll u=q.front();q.pop_front();
        for(ll k=fir[u];k!=-1;k=nxt[k])
        {
            ll vv=v[k];
            if(fl[k^1]>0)
            {
                if(dis[vv]>dis[u]-w[k] 
                || dis[vv]==-1)
                {
                    dis[vv]=dis[u]-w[k];
                    if(vis[vv]==0)    
                    {
                        if(q.empty()==0&&
                        dis[q.front()]>dis[vv])
                            q.push_front(vv);
                        else
                            q.push_back(vv);
                    }    
                    vis[vv]=1;
                }
            }
        }
        vis[u]=0;
    }
    return dis[0];
}
ll getfl(ll u,ll nowflow)
{
    vis[u]=1;
    if(u==N*2+1)return nowflow;
    ll tmp,sum=0;
    for(ll k=fir[u];k!=-1;k=nxt[k])
    {
        if(nowflow<=0)break;
        ll vv=v[k];
        if(vis[vv]==0 && fl[k]>0 
        && dis[vv]+w[k]==dis[u]&&
        (tmp=getfl(vv,min(nowflow,fl[k])))>0)
        {
            fl[k]-=tmp;
            fl[k^1]+=tmp;
            nowflow-=tmp;
            mincost+=tmp*w[k];
            sum+=tmp;
        }
     } 
    return sum;
}
int main()
{
    memset(fir,-1,sizeof(fir));
    N=read();
    for(ll i=1;i<=N;i++)
    {
        ll r=read();
        addedge(i,N*2+1,r,0);
        addedge(0,i+N,r,0);
    }
    p=read(),m=read(),f=read(),n=read(),s=read();
    for(ll i=1;i<=N;i++)
    {
        addedge(0,i,inf,p);
        if(i+m<=N)addedge(i+N,i+m,inf,f);
        if(i+n<=N)addedge(i+N,i+n,inf,s);
        if(i+1<=N)addedge(i,i+1,inf,0);
    }
    while(spfa()!=-1)
    {
        memset(vis,0,sizeof(vis));
        getfl(0,0x7fffffff);
    }
    cout<<mincost;
    return 0;
}
View Code

 



以上是关于并不对劲的餐巾计划问题的主要内容,如果未能解决你的问题,请参考以下文章

餐巾计划问题

AC日记——餐巾计划问题 洛谷 P1084

餐巾计划问题

餐巾计划问题(费用流)

网络流24题- 餐巾计划问题

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