zoukankan      html  css  js  c++  java
  • 【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
  • 相关阅读:
    Linux命令备忘录
    Redis集群部署
    Java BIO、NIO、AIO 学习
    elasticsearch性能因素总结
    tomcat配置文件server.xml详解
    Tomcat性能优化及JVM内存工作原理
    JVM性能调优监控工具jps、jstack、jmap、jhat、jstat、hprof使用详解
    HttpClient使用学习
    Java的三种代理模式
    yzoi1109&&viojs1042最小步数的一点看法——回文数
  • 原文地址:https://www.cnblogs.com/JSL2018/p/6322874.html
Copyright © 2011-2022 走看看