- 条件很多,其实是可以分开做的,有些条件在第一问,有些条件在第二问的。
- 第一问没有限制,即最大化收益,\(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;
}