订货[题解]

订货

题目描述

有一个公司,要做 \(n\) 个月的订购计划,具体如下

  • \(i\) 个月对某个产品的需求量是 \(U_i\) ,且我们已知第 \(i\) 个月该产品的订货单价为 \(d_i\)

  • 上一个月没有销售完的产品可以被贮藏起来,但是需要支付 \(m\) 的贮藏费用

  • 第一个月月初的库存为零,且第 \(n\) 个月的库存量也为零

问如何安排订购计划,才能使订购成本最低

题目分析

这道题作为一道费用流题应该还是挺明显的,而费用流模板想必就不用加以赘述,毕竟对于网络流,建图才是精髓

显然本题只有 \(n\)实点,对应 \(n\) 个月,然后我们能够发现,如果通过建立虚点我们剩余的工作将完全无法展开。

考虑一个常规想法,建立一个超级源点,一个超级汇点,则建图方案具体如下:

  • 可以将超级源点向代表月份点 \(i\) 连接一条流量是正无穷,费用是 \(d_i\)

  • 贮存可以具体化为月份\(i\)向月份 \(i+1\) 连接一条流量是 \(s\) ,费用是 \(m\) 的边

  • 最后,再将月份 \(i\) 向超级汇点连接一条流量是 \(U_i\) 费用是 \(0\) 的边

样例建图如下:

CODE

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int N=5e3+10,EDGE=1e5+10;
int n,m,s,t; 
ll maxf,minc;
inline ll read()
{
	ll s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
	return s*w;
}
ll w[EDGE],p[EDGE];
int tot=1,nex[EDGE],first[EDGE],v[EDGE];
inline void Add(int x,int y,ll z,ll c)
{
	nex[++tot]=first[x];
	first[x]=tot;
	v[tot]=y,w[tot]=z,p[tot]=c;
} 
int pre[N];
ll dis[N],Min[N]; bool vis[N];
inline bool SPFA(int S,int T)
{
	memset(dis,0x3f,sizeof(dis));
	memset(vis,false,sizeof(vis));
	queue<int> q;
	q.push(S);
	vis[S]=1,dis[S]=0,Min[S]=INF;
	while(!q.empty()){
		int now=q.front(); q.pop();
		vis[now]=false;
		for(register int i=first[now];i;i=nex[i]){
			int to=v[i];
			if(!w[i]) continue;
			if(dis[to]>dis[now]+p[i]){
				dis[to]=dis[now]+p[i];
				Min[to]=min(Min[now],w[i]);
				pre[to]=i;
				if(!vis[to]) q.push(to),vis[to]=true;
			}
		}
	}
	return dis[T]!=INF;
}
inline void dinic()
{
	while(SPFA(0,n+1)){
		minc+=dis[n+1]*Min[n+1];
		int temp=n+1,i;
		while(temp!=0){
			i=pre[temp];
			w[i]-=Min[n+1];
			w[i^1]+=Min[n+1];
			temp=v[i^1];
		}
	}
}
int main()
{
	n=read(),m=read(),s=read();
	for(register int i=1;i<=n;i++){
		int need=read();
		Add(i,n+1,need,0),Add(n+1,i,0,0);	//连接卖出边 
	}
	for(register int i=1;i<=n;i++){
		int temp=read();
		Add(0,i,INF,temp),Add(i,0,0,-temp); //连接进货边 
	}
	for(register int i=1;i<n;i++) Add(i,i+1,s,m),Add(i+1,i,0,-m); //连接储存边 
	dinic();
	printf("%lld\n",minc);
	return 0;
}
posted @ 2021-02-18 16:33  ╰⋛⋋⊱๑落叶๑⊰⋌⋚╯  阅读(44)  评论(0编辑  收藏  举报