[JZOJ P1327] [DP]订货

 

啊这是一道省选题,怪不得我不会写,最重要的是战胜自己的内心要坚信你会写出来,恩。

用f[i][j]来表示前i个月还有j吨货的费用,就是依靠 上一月的费用+上一月的货的存费+订需要的+订(j-k)的费用

f[i][j]=min(f[i-1][k]+k*m+d[i]*(u[i]+(j-k)));  ( 0<j,k<s)  ->      f[i][j]=min(f[i-1][k]-d[i]*k+k*m)+d[i]*(u[i]+j);

由于k的范围是从0到s 而s大的不止一点,所以一步步循环就会超时,因此需要用队列来优化,跟 超级教主 相似,都是单调递减,队首为最优 之后一步步把队尾踢出去换最优。

由于超级教主都是固定的值,而这玩意你只能确定每一回的值,因此需要做N遍超级教主!

先确定f[i][0] 因此就要补充,-> 上一月的存货k<这个月的需要  来算出上一月的+补充=需要的 最优。

之后为了下一月的f[i][0] 就要提前算出这个月的f[i][j]各种情况 因此再有 上一月的存货k<s  队首最优,算出f[i][j]

答案就为f[n][0]。

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m,s,u[55],d[55];
int f[52][10010],que[100010];
int head=0,tail=0;
void work(int i,int k)
{
    while(head<tail&&(f[i-1][k]+k*(m-d[i])<f[i-1][que[tail-1]]+que[tail-1]*(m-d[i])))
        tail--;
    que[tail++]=k;
}
int main()
{
cin>>n>>m>>s;
    for(int i=1;i<=n;i++)    cin>>u[i];
    for(int i=1;i<=n;i++)    cin>>d[i];
    memset(f,10,sizeof(f));
    f[0][0]=0;
    for(int i=1;i<=n;i++)
    {
        head=tail=0;
        for(int k=0;k<=u[i];k++)    work(i,k);
        int t=que[head];
        f[i][0]=f[i-1][t]+t*(m-d[i])+d[i]*u[i];
        for(int j=1;j<=s;j++)
        {
            if(j+u[i]<=s)    work(i,j+u[i]);
            int k=que[head];
            f[i][j]=f[i-1][k]+k*m+d[i]*(u[i]+j-k);
        }
    }
    cout<<f[n][0];
return 0;
}
你好蠢居然还要看code

 

调试时莫名出现输入输出,一个bug吧,这道题队列应该是最优解法,然而姬树流大大早已想出简单DP,然而看不懂,还有一个做法是费用流,费用流是什么鬼。以后补上。

 

posted @ 2017-01-20 17:31  kaike  阅读(127)  评论(0编辑  收藏  举报