这个题非常繁琐,而且网上的题解非常不详细。。
首先第一问
由于所有的点都要走到,所以就是01背包
需要可持久化背包来输出方案。。类似floyd的判断方式
然后这些选定的点都一定是要维护的,
第二问就是枚举每个点+每个点的加速器数量 来 dp
然后n^4的dp就是从1到n枚举i,从1到i枚举j,从0到r枚举j的加速器个数k ,从0到r枚举i的加速器个数l,然后转移
然后可以优化 ,由于对于 枚举的每一个j 当k相同的时候只从j中选最优的更新 ,只要 求i对于每个k的前面的最优值即可,可以证明这是贪心取,(只要合法,就去花费最小的)
然后就顺带出一个类似飞扬的小鸟的更新方法,同级更新,一直选择最优的往上买,,
对于买加速器的情况就变成O(1)的顺带转移了
码:
#include<iostream>
#include<cstdio>
using namespace std;
int n,m,r,l0,k,v[4002],ans1,ans2=1000000009,c[4002],l[4002],p[4002],wh[4002],i,j,f[4003][4003],q[5003][5003],z1[5003],z2[5003];
bool bj[4002];
int main()
{
scanf("%d%d%d%d",&n,&m,&r,&l0);
r=min(r,n*2);
for(i=1;i<=n;i++)
{
scanf("%d%d%d%d%d",&c[i],&v[i],&l[i],&p[i],&wh[i]);
}
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
f[j][i]=-1000000009;
f[0][0]=0;
for(j=1;j<=n;j++)
{
for(i=m;i>=0;i--)
{
f[j][i]=f[j-1][i];
if(i>m-c[j])continue;
if(f[j][i+c[j]]<f[j-1][i]+v[j])
{
f[j][i+c[j]]=f[j-1][i]+v[j];
if(f[j][i+c[j]]>ans1)
{
ans1=f[j][i+c[j]];
ans2=i+c[j];
}
}
}
}
int lin=ans1,lin2=ans2;
if(lin2<1000000009) for(i=n;i>=1;i--)
{
if(lin2-c[i]>=0&&f[i-1][lin2-c[i]]+v[i]==lin)
{
bj[i]=1;
lin=f[i-1][lin2-c[i]];
lin2=lin2-c[i];
}
}
//ans1=f[ans1][0];
for(i=0;i<=n;i++)
for(j=0;j<=r+2;j++)
f[i][j]=1000000009,z2[j]=-1;
ans2=1000000009;
f[0][r]=0;
q[r][++z2[r]]=0;
for(i=1;i<=n;i++)
{
for(j=2;j<=r+2;j++)//枚举上一个更新这一个
{
if(j!=2&&p[i]>0)f[i][j-2]=f[i][j-3]+p[i];
while(z1[j]<=z2[j]&&(l[i]-l[q[j][z1[j]]]>l0))z1[j]++;
if(j<=r)
{
int l1=l[i]-l[q[j][z1[j]]];
if(l0>=l1)
if(f[i][j-2]>f[q[j][z1[j]]][j])
f[i][j-2]=f[q[j][z1[j]]][j];
}
while(z1[j-2]<=z2[j-2]&&l[i]-l[q[j-2][z1[j-2]]]>l0)
z1[j-2]++;
if(bj[i])while(z2[j-2]>=z1[j-2])z1[j-2]++;
while(z1[j-2]<=z2[j-2]&&f[i][j-2]+wh[i]<f[q[j-2][z2[j-2]]][j-2])z2[j-2]--;
q[j-2][++z2[j-2]]=i;
}
for(j=0;j<=r;j++)
f[i][j]+=wh[i];
}
for(i=0;i<=r;i++)
if(f[n][i]<ans2)ans2=f[n][i];
if(ans2<1000000009)printf("%d %d",ans1,ans1-ans2);
else printf("Poor Coke!");
}