[[HNOI2005]星际贸易]

链接 [HNOI2005]星际贸易
  • 条件很多,其实是可以分开做的,有些条件在第一问,有些条件在第二问的。
  • 第一问没有限制,即最大化收益,\(f_{i,j}\)表示前\(i\)个地点剩下\(j\)个东西没卖,背包即可。
  • 第二问基于第一问的选择,把第一问的转移扣出来即可,这里要最小化代价,设\(g_{i,j}\)表示当前在\(i\)地点,燃料剩余\(j\)的最小花费,那么有:
  • \[g_{i,j}=min(g_{i,j-1}+p_i)\]
  • 这是类似于多重背包的转移,也就是不停的在这里买燃料,因为从小往大更新的,所以省去了买多少的枚举复杂度。
  • \[g_{i,j}=min(g_{k,j+2}+F_i)\]
  • 表示从别的地方过来,消耗两个燃料,然后修花费\(F_i\),注意这里满足\(L \leq Dis_i-Dis_k\)\(k\)在最近一个必经点之后。
  • 直接转移复杂度\(O(n^3)\),发现维护前缀最小值,但是距离有限制,可以单调队列优化成\(O(n^2)\)
#include<bits/stdc++.h>
#define R register int
#define ll long long
#define db double 
using namespace std;
const int N=4005;
const int inf=2e9;
int n,m,r,L,ans,pos,yes[N],f[N][N],pr[N][N],id[N];
int tl[N],hd[N],G[N][N],a[N],b[N],l[N],P[N],F[N];
int gi(){
    R x=0,k=1;char c=getchar();
    while(c!='-'&&(c<'0'||c>'9'))c=getchar();
    if(c=='-')k=-1,c=getchar();
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar(); 
    return x*k;
}
int main(){
    freopen("2.in","r",stdin);
    n=gi(),m=gi(),r=min(2*n,gi()),L=gi();
    for(R i=1;i<=n;++i)
        a[i]=gi(),b[i]=gi(),l[i]=gi(),P[i]=gi(),F[i]=gi();
    for(R i=1;i<n;++i)
        if(l[i+1]-l[i]>L)return puts("Poor Coke!"),0;
    memset(f,-63,sizeof(f)),f[0][m]=0;
    for(R i=1;i<=n;++i){
        for(R j=0;j<=m;++j)f[i][j]=f[i-1][j];
        for(R j=0;j+a[i]<=m;++j)
            f[i][j]=max(f[i][j],f[i-1][j+a[i]]+b[i]);
    }
    for(R j=0;j<=m;++j)if(f[n][j]>ans)ans=f[n][j],pos=j;
    for(R i=n,j=pos;i>=1;--i){
        if(f[i-1][j]==f[i][j])continue;
        yes[i]=1,j+=a[i];
    }
    memset(f,63,sizeof(f)),f[0][r]=0,G[r][tl[r]++]=0;
    for(R i=1;i<=n;++i)
        for(R j=0;j<=r;++j){
            if(j&&P[i])f[i][j]=min(f[i][j],f[i][j-1]+P[i]);
            if(hd[j+2]<tl[j+2])f[i][j]=min(f[i][j],f[G[j+2][hd[j+2]]][j+2]+F[i]);
            if(yes[i])hd[j]=tl[j]=0;
            while(hd[j]<tl[j]&&f[i][j]<f[G[j][tl[j]-1]][j])tl[j]--;
            G[j][tl[j]++]=i;
            while(hd[j]<tl[j]&&l[i+1]-l[G[j][hd[j]]]>L)hd[j]++;
        }
    R res=f[0][0];
    for(R j=0;j<=r;++j)res=min(res,f[n][j]);
    if(res==f[0][0])puts("Poor Coke!");
    else printf("%d %d\n",ans,ans-res);
    return 0;
}

转载于:https://www.cnblogs.com/Tyher/p/9845452.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值