Description
某公司估计市场在第i个月对某产品的需求量为Ui,已知在第i月该产品的订货单价为di,上个月月底未销完的单位产品要付存贮费用m,假定第一月月初的库存量为零,第n月月底的库存量也为零,问如何安排这n个月订购计划,才能使成本最低?每月月初订购,订购后产品立即到货,进库并供应市场,于当月被售掉则不必付存贮费。假设仓库容量为S。
Input
第1行:n, m, S (0<=n<=50, 0<=m<=10, 0<=S<=10000)
第2行:U1 , U2 , … , Ui , … , Un (0<=Ui<=10000)
第3行:d1 , d2 , …, di , … , dn (0<=di<=100)
Output
只有1行,一个整数,代表最低成本
Sample Input
3 1 1000
2 4 8
1 2 4
Sample Output
34
HINT
传送门
一天买一定量以上的货品,累计到下一天单位价值m……
非常熟悉的一个东西(然而我并没有一眼看出来)
这个货品的数目可以抽象为“流”,
假设有一个源点,不断发出流,流量为f,
发到一个点x之后,产生了
D[x]∗f
的价值,
然后要满足
f>=U[x]
,假如有剩余,则
f−U[x]
的流会流向下一个点,
并且产生了
(f−U[x])∗m
的价值。
那么构图就很容易了,建立一个超级源,连接向每一个点,
这里的点就是指1~n的每天,
然后连接x点单位流量价值D[x],容量
∞
,
第x天和第(x+1)天连接,单位流量价值m,容量S(最多仓库可以积累S),
最后1~n天和汇点连接,价值是0,但容量是U[x],
为什么这么设置容量呢?我们知道U[x]都是要满足的,
所以这样的话,跑最小费用最大流就可以直接满足条件了。
然后说说动态规划的做法。
我第一眼还是想到了dp……但是思路很不清晰导致写不出来,
于是就重新想了一遍……
最后还是大佬理了一下思路结果豁然开朗
(orz……)
其实dp也是可以的,而且很简单,dp[i][j]表示前i天后仓库剩余j货品的最小花费。
但是事实上有一些细节,那就规定:
dp[i][j]表示前i天,仓库剩余货品减去U[i]后还剩j,并且付掉了j货品保存费用的最小花费。
假设考虑“减去U[i]”,“付掉保存j货品费用”这些都去掉的最小答案是g[i][j]
那么:
g[i][j]=min{f[i−1][k]+D[i]∗(j−k),k<=j}
1<=i<=n,0<=j<=S+U[i]
然后其实g[i][j]和f[i][j]之间是可以推导的。
f[i][j]=g[i][j−U[i]]+(j−U[i])∗m
时间复杂度O(n*S^2)。
优化很简单,观察g[i][j]的式子,
分离之后就可以直接(似乎单调栈)维护了。
代码是费用流的……dp不想码了QAQ
2333跑得比dp还快
#include<bits/stdc++.h>
using namespace std;
const int
N=55,
M=220,
inf=2000000000;
int n,m,S,Ecnt;
int dis[N],pre[N],U[N],D[N];
bool vis[N];
struct Edge{
int next,from,to,C,cost;
}E[M<<1];int head[N];
void add(int u,int v,int C,int x){
E[Ecnt].next=head[u];
E[Ecnt].from=u,E[Ecnt].to=v;
E[Ecnt].C=C,E[Ecnt].cost=x;
head[u]=Ecnt++;
}
int SPFA(int s,int t){
memset(pre,0,sizeof(pre));
memset(dis,100,sizeof(dis));
int tinf=dis[1];
queue<int>Q;
while (!Q.empty()) Q.pop();
Q.push(s),dis[s]=0;
while (!Q.empty()){
int u=Q.front();Q.pop();
vis[u]=0;
for (int i=head[u];~i;i=E[i].next)
if (E[i].C){
int v=E[i].to;
if (dis[u]+E[i].cost<dis[v]){
dis[v]=dis[u]+E[i].cost;
pre[v]=i;
if (!vis[v]) vis[v]=1,Q.push(v);
}
}
}
if (dis[t]<tinf) return 1;
return 0;
}
int MCMF(int s,int t){
int ans=0;
while (SPFA(s,t)){
int f=inf;
for (int i=t;i!=s;i=E[pre[i]].from)
f=min(f,E[pre[i]].C);
for (int i=t;i!=s;i=E[pre[i]].from)
E[pre[i]].C-=f,E[pre[i]^1].C+=f;
ans+=dis[t]*f;
}
return ans;
}
int main(){
scanf("%d%d%d",&n,&m,&S);
for (int i=1;i<=n;i++) scanf("%d",&U[i]);
for (int i=1;i<=n;i++) scanf("%d",&D[i]);
int source=n+1,sink=n+2;
memset(head,255,sizeof(head));
for (int i=1;i<=n;i++)
add(source,i,inf,D[i]),add(i,source,0,-D[i]),
add(i,sink,U[i],0),add(sink,i,0,0);
for (int i=1;i<n;i++)
add(i,i+1,S,m),add(i+1,i,0,-m);
printf("%d\n",MCMF(source,sink));
return 0;
}