将问题分为两次 dp。
题面有:“只有一种获得最大贸易额的方法”
所以直接说明了贸易额与那些费用无关。
有考虑到无论干啥都要维护,第二次 d p dp dp 直接以暗物质为核心即可
对于这里 R R R 与 n ∗ 2 n*2 n∗2 取 m i n min min 的一些细节理解。
我们设计状态,因为观察到了暗物质最多也就使用 n ∗ 2 n*2 n∗2 个,与 R R R 如此之大的范围无关
第二次 d p dp dp 中, f i , j f_{i,j} fi,j 表示到了第 i i i 颗星球,还有 j j j 暗物质的最小花费
正确性:
-
r > n ∗ 2 r > n*2 r>n∗2 时,直接不买
-
r ≤ n ∗ 2 r \le n*2 r≤n∗2 时,做 d p dp dp,由大于 n ∗ 2 n*2 n∗2 的位置转移过来是不合法的
对于第二次 dp 中,要进行销售的星球与要买暗物质的星球的维护费用冲突的解决
对于考虑暗物质是建立在确定了那些是要销售的星球的基础上的,所以第二次 d p dp dp 对于这些星球的维护费用不应该考虑。
由于我们用单调队列模拟这个滑动窗口的过程,当跑到被选为要销售的星球时,直接 h e a d [ j ] = t a i l [ j ] = 0 head[j] = tail[j] = 0 head[j]=tail[j]=0。
所以这里 d p dp dp 可以理解为在每两颗被选星球中间做选择。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int N = 2010;
int n,m,r,L0;
int ans1,ans2;
bool mark[N];
int dp[N][N];
struct Star{ int goal,val,dis,price,cost; }a[N];
void DP(){
memset(dp,-1,sizeof dp);
dp[0][0] = 0;
for(int i = 1;i<=n;++i)
for(int j = 0;j<=m;++j){
if(dp[i-1][j] >= 0)dp[i][j] = dp[i-1][j];
if(j >= a[i].goal && dp[i-1][j-a[i].goal] >= 0)
dp[i][j] = max(dp[i-1][j-a[i].goal] + a[i].val,dp[i][j]);
}
int res = 0;
for(int i = 0;i<=m;++i){
// print(dp[n][i]);
if(dp[n][i] > dp[n][res])res = i;
}
for(int i = n,j = res;i;--i){
if(dp[i][j] == dp[i-1][j])continue;
else mark[i] = 1, j -= a[i].goal;
}
// print(res);
ans1 = dp[n][res];
}
int f[N][N];
int tail[N],q[N][N],head[N];
void solve(){
memset(f,0x3f,sizeof f);
f[0][r] = 0;
q[r][++tail[r]] = 0;
for(int i = 1;i<=n;++i){
for(int j = 0;j<=r;++j){
if(a[i].price && j)f[i][j] = min(f[i][j],f[i][j-1] + a[i].price);
if(tail[j+2] > head[j+2])f[i][j] = min(f[i][j],f[q[j+2][head[j+2]]][j+2] + a[i].cost);
if(mark[i])head[j] = tail[j] = 0;
while(head[j] < tail[j] && f[q[j][tail[j]-1]][j] >= f[i][j])--tail[j];
q[j][tail[j]++] = i;
while(head[j] < tail[j] && a[i+1].dis - a[q[j][head[j]]].dis > L0)++head[j];
}
}
int res = 0;
for(int i = 0;i<=r;++i)if(f[n][i] < f[n][res])res = i;
if(f[n][res] == inf)puts("Poor Coke!");
else printf("%d %d",ans1,ans1-f[n][res]);
}
int main(){
scanf("%d%d%d%d",&n,&m,&r,&L0);
r = min(r,n<<1);
a[0] = {0,0,0,0,0};
for(int i = 1;i<=n;++i)
scanf("%d%d%d%d%d",&a[i].goal,&a[i].val,&a[i].dis,&a[i].price,&a[i].cost);
for(int i = 1;i<=n;++i)if(a[i].dis - a[i-1].dis > L0)return puts("Poor Coke!"),0;
DP();
solve();
return 0;
}