题解报告——软件开发

题目传送门

题目描述

某软件公司正在规划一项n天的软件开发计划,根据开发计划第i天需要ni个软件开发人员,为了提高软件开发人员的效率,公司给软件人员提供了很多的服务,其中一项服务就是要为每个开发人员每天提供一块消毒毛巾,这种消毒毛巾使用一天后必须再做消毒处理后才能使用。消毒方式有两种,A种方式的消毒需要a天时间,B种方式的消毒需要b天(b>a),A种消毒方式的费用为每块毛巾fA, B种消毒方式的费用为每块毛巾fB,而买一块新毛巾的费用为f(新毛巾是已消毒的,当天可以使用);而且f>fA>fB。公司经理正在规划在这n天中,每天买多少块新毛巾、每天送多少块毛巾进行A种消毒和每天送多少块毛巾进行B种消毒。当然,公司经理希望费用最低。

你的任务就是:为该软件公司计划每天买多少块毛巾、每天多少块毛巾进行A种消毒和多少毛巾进行B种消毒,使公司在这项n天的软件开发中,提供毛巾服务的总费用最低。

输入输出格式

输入格式:

第1行为n,a,b,f,fA,fB.

第2行为n1,n2,……,nn. (注:1≤f,fA,fB≤60,1≤n≤1000)

输出格式:

最少费用

输入输出样例

输入样例#1: 复制
4  1  2  3  2  1                         
8  2  1  6  
输出样例#1: 复制
38

【思路分析】
首先看到这道题时,“这不就是一个线性DP吗??!!”,然后果断开码,写了一半发现,哎呀!这转移方程咋个奇奇妙妙的啊?貌似我的DP GG了,只有去看题解。。。
本以为可以看到很妙妙的DP操作,结果映入眼帘的TM是“费用流??!!”仿佛命运给我开了个大大的玩笑,太假了。。。
好嘛那么就来看看我们这道题咋个建图吧。。。
  1. 拆点,将每个点拆成入点和出点,表示为i和i+n;

  2. 考虑最后每天都有且只有ni个餐巾,所以建立源点S和汇点T,并在S和i之间连容量为INF、费用为0的边,

同理在i+n和T之间连容量为INF、费用为0的边。

  1. 考虑几个方案:对于a和b,只要在i天和i+a天之间连一条容量为INF、费用为fa的边,b同理;

  2. 考虑一天的餐巾可以原封不动的留到明天,故可以在i和i+1之间连一条容量为INF,费用为0的边;

  3. 最后为了保证每天能有足够的餐巾(每条汇弧饱和),可以在S在每个i+n之间加一条容量为INF、费用为f的边;

最后简单地跑一遍费用流即可;



【代码实现】
#include<cstdio>
#include<cctype>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
void read(int &v)
{
    int f;char ch;
    while(!isdigit(ch=getchar())&&ch!='-'); ch=='-'?(f=-1,v=0):(f=1,v=ch-'0');
    while(isdigit(ch=getchar())) v=v*10+ch-'0';v=v*f;
}
const int N=2e3+5,INF=1e9+7;
struct sd{
    int next,to,w,exp;
    sd(){};
    sd(int a,int b,int c,int d){next=a,to=b,w=c,exp=d;}
}edge[N*100];
queue<int> que;
int head[N],pre[N],last[N],dis[N],flow[N],n,A,B,f,fa,fb,cnt=1,s,t;
bool vis[N];
ll ans;
void add_edge(int from,int to,int w,int exp)
{
    edge[++cnt]=sd(head[from],to,w,exp),head[from]=cnt;
    edge[++cnt]=sd(head[to],from,0,-exp),head[to]=cnt;
}
bool spfa()
{
    memset(dis,127,sizeof(dis));
    memset(flow,127,sizeof(flow));
    pre[t]=-1,dis[s]=0,vis[s]=1,que.push(s);
    while(!que.empty())
    {
        int v=que.front();que.pop(),vis[v]=0;
        for(int i=head[v];i;i=edge[i].next)
        {
            int to=edge[i].to;
            if(dis[to]>dis[v]+edge[i].exp&&edge[i].w>0)
            {
                dis[to]=dis[v]+edge[i].exp;
                last[to]=i,pre[to]=v;
                flow[to]=min(flow[v],edge[i].w);
                if(!vis[to]) que.push(to),vis[to]=1;
            }
        }
    }
    return pre[t]!=-1;
}
int main()
{
    read(n),read(A),read(B),read(f),read(fa),read(fb);
    s=0,t=2*n+1;
    for(int i=1;i<=n;i++)
    {
        int nn;read(nn);
        add_edge(s,i,nn,0),add_edge(i+n,t,nn,0);
        add_edge(s,i+n,INF,f);
        if(i+A+1<=n) add_edge(i,i+A+1+n,INF,fa);
        if(i+B+1<=n) add_edge(i,i+B+1+n,INF,fb);
        if(i!=n) add_edge(i,i+1,INF,0);
    }
    while(spfa())
    {
        int id=t;
        ans+=1ll*dis[t]*flow[t];
        while(id!=s)
        {
            edge[last[id]].w-=flow[t];
            edge[last[id]^1].w+=flow[t];
            id=pre[id];
        }
    }
    printf("%lld",ans);
    return 0;
}



原文地址:https://www.cnblogs.com/genius777/p/9694939.html