HAOI2010订货

Posted cdcq(本博客废弃!现用博客:https://www.cn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HAOI2010订货相关的知识,希望对你有一定的参考价值。

可以DP也可以是费用流,然而被我用非常简单的DP破了【开心】

原题:

某公司估计市场在第i个月对某产品的需求量为Ui,已知在第i月该产品的订货单价为di,上个月月底未销完的单位产品要付存贮费用m,假定第一月月初的库存量为零,第n月月底的库存量也为零,问如何安排这n个月订购计划,才能使成本最低?每月月初订购,订购后产品立即到货,进库并供应市场,于当月被售掉则不必付存贮费。假设仓库容量为S。

0<=n<=50,0<=m<=10,0<=S<=10000,0<=Ui<=10000,0<=di<=100

 

恩紫萱的讲解说要用队列优化DP,我太弱没看懂,自己想出了一个我觉得比较妙的方法DP掉了

核心思路是在买东西的时候f[i]只与f[i-1]的最优值有关(注意是最优值)

然后要买的时候从1到S顺推,f[i]=min(f[i],f[i-1]+d[i])

恩原理我语文不好,直接丢个图吧

可以理解为f[i-1]代表了1到i-1所有的方案,但是都没有f[i]优,再往上考虑的话,由于增加的费用都是一样的,所以和f[1]到f[i-1]有关的方案不会优于f[i]相关的方案,就只考虑f[i]了

如果f[i]不是更优,f[i-1]代表了f[1]到f[i-1]的方案,只管使用即可

然后还有许多需要注意的细节(我就是因为细节问题拖了很久才A quq)

首先货物是可以进货不入库直接卖,不占用仓库空间,所以实际上仓库有M+a[i]的储存空间(每个月的需求也可能超过仓库容量),然后每个月结束的时候f中1到M不用处理,下个月直接转移即可,但是M+1到M+a[i]这一段一定要处理成正无穷,不然会影响到下个月(我就是因为这个细节卡了一下午,差点弃疗quq)

还有更多的细节问题,大家自己体验吧一。一

DP代码非常短

代码(DP):

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 using namespace std;
 7 int read(){int z=0,mark=1;  char ch=getchar();
 8     while(ch<\'0\'||ch>\'9\'){if(ch==\'-\')mark=-1;  ch=getchar();}
 9     while(ch>=\'0\'&&ch<=\'9\'){z=(z<<3)+(z<<1)+ch-\'0\';  ch=getchar();}
10     return z*mark;
11 }
12 const int oo=168430090;
13 int n,m,ns;  int a[110],b[110];
14 int f[21000];
15 int main(){//freopen("ddd.in","r",stdin);
16     memset(f,10,sizeof(f));
17     cin>>n>>ns>>m;
18     for(int i=1;i<=n;i++)  a[i]=read();
19     for(int i=1;i<=n;i++)  b[i]=read();
20     f[0]=0;  a[0]=0;
21     for(int i=1;i<=n;i++){
22         for(int j=0;j<=m;j++)  f[j]=f[j+a[i-1]]+j*ns;
23         for(int j=m+1;j<=m+a[i-1];j++)  f[j]=oo;
24         for(int j=1;j<=m+a[i];j++)  f[j]=min(f[j],f[j-1]+b[i]);
25     }
26     cout<<f[a[n]]<<endl;
27     return 0;
28 }
View Code

======================================================下面是费用流======================================================

至于费用流呐,建图也非常好想

首先利用割的思想,每个月到汇一条流量为需求,费用0的边,割掉这条边表示这个月的费用满足了

然后源到每个月一条流量正无穷,费用为这个月进货费用的边,表示物品可以随意买

每个月到下个月一条流量为仓库容量,费用为储存话费的边,表示上个月的货可以继承到下个月

至于我是怎么想出来的……直觉……我觉得还要继续发掘一下这其中潜在的思路规律

代码也很简单,标准的费用流模板:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 using namespace std;
 7 int read(){int z=0,mark=1;  char ch=getchar();
 8     while(ch<\'0\'||ch>\'9\'){if(ch==\'-\')mark=-1;  ch=getchar();}
 9     while(ch>=\'0\'&&ch<=\'9\'){z=(z<<3)+(z<<1)+ch-\'0\';  ch=getchar();}
10     return z*mark;
11 }
12 const int oo=168430090;
13 struct ddd{int next,y,evalue,rev,cost;}e[1100000];  int LINK[510000],ltop=0;
14 inline void insert(int x,int y,int z,int _cost){
15     e[++ltop].next=LINK[x];LINK[x]=ltop;e[ltop].y=y;e[ltop].evalue=z;e[ltop].rev=ltop+1;e[ltop].cost=_cost;
16     e[++ltop].next=LINK[y];LINK[y]=ltop;e[ltop].y=x;e[ltop].evalue=0;e[ltop].rev=ltop-1;e[ltop].cost=-_cost;
17 }
18 int n,m,ns;  int s,t;
19 int dist[510000];
20 int QUEUE[510000],head=0;  bool visited[510000];
21 int last[510000],last_e[510000];
22 bool spfa(){
23     memset(visited,0,sizeof(visited));
24     memset(dist,10,sizeof(dist));
25     QUEUE[head=1]=s;  dist[s]=0;
26     for(int k=1;k<=head;k++){
27         for(int i=LINK[QUEUE[k]];i;i=e[i].next)
28             if(e[i].evalue && dist[QUEUE[k]]+e[i].cost<dist[e[i].y]){
29                 dist[e[i].y]=dist[QUEUE[k]]+e[i].cost;
30                 last[e[i].y]=QUEUE[k],last_e[e[i].y]=i;
31                 if(!visited[e[i].y])  QUEUE[++head]=e[i].y,visited[e[i].y]=true;
32             }
33         visited[QUEUE[k]]=false;
34     }
35     return dist[t]<oo;
36 }
37 int cost_flow(){
38     int bowl=0;
39     while(spfa()){
40         int min_flow=oo;
41         for(int i=t;i!=s;i=last[i])  min_flow=min(min_flow,e[last_e[i]].evalue);
42         for(int i=t;i!=s;i=last[i]){
43             bowl+=min_flow*e[last_e[i]].cost;
44             e[last_e[i]].evalue-=min_flow,e[e[last_e[i]].rev].evalue+=min_flow;
45         }
46     }
47     return bowl;
48 }
49 int main(){//freopen("ddd.in","r",stdin);
50     cin>>n>>ns>>m;  s=0,t=n+1;
51     for(int i=1;i<=n;i++)  insert(i,t,read(),0);
52     for(int i=1;i<=n;i++)  insert(s,i,oo,read());
53     for(int i=1;i<n;i++)  insert(i,i+1,m,ns);
54     cout<<cost_flow()<<endl;
55     return 0;
56 }
View Code

 

以上是关于HAOI2010订货的主要内容,如果未能解决你的问题,请参考以下文章

bzoj2424 [HAOI2010]订货

费用流P2517 [HAOI2010]订货

[HAOI2010]订货

bzoj2424: [HAOI2010]订货(费用流)

BZOJ2424[HAOI2010]订货 最小费用流

bzoj2424: [HAOI2010]订货