[DP/单调队列]BZOJ 2059 [Usaco2010 Nov]Buying Feed 购买饲料

首先我想吐槽的是题目并没有表明数据范围。。。

 

这个题目 DP方程并不难表示。

dp[i][j]表示前i个地点携带了j个货物的最小花费

dp[i][j] = dp[i-1][k] + (j-k) * cost + j*j*(leng[i]-leng[i-1])

如果你这样直接提交上去,恭喜你超时!!! 因为这个时间复杂度是 O(n*k^2)

所以我们需要优化一下,可以发现式子可以化简为:

dp[i][j] = dp[i-1][k] - k * cost  + j*j*(leng[i]-leng[i-1]) +  j*cost 

dp[i][j] = dp[i-1][k] - k * cost 这一部分可以只是与k有关,这里我们可以用单调队列进行优化,使其保持 最小值。

 

坑点1:注意数据范围

坑点2:注意初始化

 

/**************************************************************
    Problem: 2059
    User: LYFer
    Language: C++
    Result: Accepted
    Time:480 ms
    Memory:61600 kb
****************************************************************/
 
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
 
#define mp(a,b) make_pair(a,b)
#define fr(a,b,c) for(int c=a;c<=b;++c)
 
using namespace std;
typedef long long ll;
 
const ll INF = 1e16;
ll dp[777][10005];
int n,e,k;
 
 
inline int Read(){
    int ans = 0, flag = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if(ch=='-') flag=-1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        ans = ans * 10 + ch - '0';
        ch = getchar();
    }
    return ans * flag;
}
 
struct task{
    ll now,num,cost;
    bool operator <(const task &x) const{
        return now < x.now;
    }
}feed[505];
 
struct DanDiao{
    deque< pair<ll,int> >Q;
    void insert(ll x,int y){
        while( !Q.empty() && Q.back().first >= x) Q.pop_back();
        Q.push_back( mp(x,y) );
    }
    void erase(int y){
        while( !Q.empty() && Q.front().second <= y) Q.pop_front();
    }
}DD;
 
int main(){
    k = Read();
    e = Read();
    n = Read();
    fr(1,n,i){
        feed[i].now = Read();
        feed[i].num = Read();
        feed[i].cost = Read();
    }
    sort(feed+1,feed+1+n);
    feed[++n] = (task){e,0,0};
    for(int i=0;i<=n;i++){
        for(int j=0;j<=k;j++){
            dp[i][j] = INF;
        }
    }
    dp[1][0] = 0;
    fr(2,n,i){
        DD.Q.clear();
        int r = 0;
        fr(0,k,j){
            while(r <= j) DD.insert(dp[i-1][r] - r*feed[i-1].cost , r) , r++;
            DD.erase(j - feed[i-1].num - 1);
            if( DD.Q.empty()) dp[i][j] = INF;
            else dp[i][j] = DD.Q.front().first+j*feed[i-1].cost+j*j*(feed[i].now-feed[i-1].now);
        }
    }
    /*for(int i=1;i<=n;i++){
        for(int j=0;j<=k;j++){
            printf("dp[%d][%d]:%d\n",i,j,dp[i][j]);
        }
    }*/
    printf("%lld\n",dp[n][k]);
    return 0;
}
AC代码

 

转载于:https://www.cnblogs.com/OIerLYF/p/7601218.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值