题意:直接看题意即可
题解:首先,第一步可以想到的是dp,用dp[i][j]表示在第i天下有j张票的最大钱数,然后按照时间顺序dp下去,复杂度o(t^2mp^2),只能得到40分。
附上代码:
#include<bits/stdc++.h>
using namespace std;
const int MAX=2e3+50;
int t,mp,w;
int dp[MAX][MAX];
int vb[MAX],vs[MAX],lb[MAX],ls[MAX];
int main()
{
scanf("%d%d%d",&t,&mp,&w);
for(int i=1;i<=t;i++){
scanf("%d%d%d%d",&vb[i],&vs[i],&lb[i],&ls[i]);
}
memset(dp,-63,sizeof(dp));
dp[0][0]=0;
for(int i=1;i<=t;i++){
for(int j=0;j<=max(0,i-w-1);j++){
for(int k=0;k<=mp;k++){
for(int l=0;k+l<=mp&&l<=lb[i];l++){
dp[i][k+l]=max(dp[i][k+l],dp[j][k]-l*vb[i]);
}
for(int l=0;l<=k&&l<=ls[i];l++){
dp[i][k-l]=max(dp[i][k-l],dp[j][k]+l*vs[i]);
}
}
}
}
int ans=0;
for(int i=1;i<=t;i++){
ans=max(ans,dp[i][0]);
}
cout<<ans<<endl;
return 0;
}
其实没必要枚举限制天数,这样想,dp[i][j]是在第i天拥有j个股票的最大获利,那么直接可以递推前一天dp[i][j]=max(dp[i][j],dp[i-1][j])即可,这样可以拿到50分
附上代码:
#include<bits/stdc++.h>
using namespace std;
const int MAX=2e3+50;
int t,mp,w;
int dp[MAX][MAX];
int vb[MAX],vs[MAX],lb[MAX],ls[MAX];
int main()
{
scanf("%d%d%d",&t,&mp,&w);
for(int i=1;i<=t;i++){
scanf("%d%d%d%d",&vb[i],&vs[i],&lb[i],&ls[i]);
}
memset(dp,-63,sizeof(dp));
dp[0][0]=0;
for(int i=1;i<=t;i++){
int j=max(0,i-w-1);
for(int k=0;k<=mp;k++){
dp[i][k]=max(dp[i][k],dp[i-1][k]);
for(int l=0;k+l<=mp&&l<=lb[i];l++){
dp[i][k+l]=max(dp[i][k+l],dp[j][k]-l*vb[i]);
}
for(int l=0;l<=k&&l<=ls[i];l++){
dp[i][k-l]=max(dp[i][k-l],dp[j][k]+l*vs[i]);
}
}
}
cout<<dp[t][0]<<endl;
return 0;
}
其实还可以发现,在前W天只能买或者不买,那么再优化一下,就可以拿到70分了
附上代码:
#include<bits/stdc++.h>
using namespace std;
const int MAX=2e3+50;
int t,mp,w;
int dp[MAX][MAX];
int vb[MAX],vs[MAX],lb[MAX],ls[MAX];
int main()
{
scanf("%d%d%d",&t,&mp,&w);
for(int i=1;i<=t;i++){
scanf("%d%d%d%d",&vb[i],&vs[i],&lb[i],&ls[i]);
}
memset(dp,-63,sizeof(dp));
dp[0][0]=0;
for(int i=1;i<=t;i++){
for(int j=0;j<=lb[i];j++){
dp[i][j]=-j*vb[i];
}
for(int j=0;j<=mp;j++){
dp[i][j]=max(dp[i][j],dp[i-1][j]);
}
if(i<=w){
continue;
}
int j=max(0,i-w-1);
for(int k=0;k<=mp;k++){
dp[i][k]=max(dp[i][k],dp[i-1][k]);
for(int l=0;k+l<=mp&&l<=lb[i];l++){
dp[i][k+l]=max(dp[i][k+l],dp[j][k]-l*vb[i]);
}
for(int l=0;l<=k&&l<=ls[i];l++){
dp[i][k-l]=max(dp[i][k-l],dp[j][k]+l*vs[i]);
}
}
}
cout<<dp[t][0]<<endl;
return 0;
}
最后可以发现,如果买股票的话,对于每个dp[i][j]=max(dp[x][k]-(j-k)*v),展开为dp[i][j]=max(dp[x][k]+k*v-j*v),又可以发现j*v始终是j*v,所以可以移动外面,dp[i][j]=max(dp[x][k]+k*v)-j*v,所以此时通过单调队列进行维护max(dp[x][k]+k*v),同理,如果卖股票的话,dp[i][j]=max(dp[x][k]+(k-j)*v),展开dp[i][j]=max(dp[x][k]+k*v-j*v),发现j*v没关系,转到外面,dp[i][j]=max(dp[x][k]+k*v)+j*v,然后,还是通过单调队列进行维护max(dp[x][k]+k*v),而此时的方向需要从后往前进行维护。
附上代码:
#include<bits/stdc++.h>
using namespace std;
const int MAX=2e3+50;
int t,mp,w;
int dp[MAX][MAX];
int vb[MAX],vs[MAX],lb[MAX],ls[MAX];
int deq[MAX];
int main()
{
scanf("%d%d%d",&t,&mp,&w);
for(int i=1;i<=t;i++){
scanf("%d%d%d%d",&vb[i],&vs[i],&lb[i],&ls[i]);
}
memset(dp,-63,sizeof(dp));
dp[0][0]=0;
for(int i=1;i<=t;i++){
for(int j=0;j<=lb[i];j++){
dp[i][j]=-j*vb[i];
}
for(int j=0;j<=mp;j++){
dp[i][j]=max(dp[i][j],dp[i-1][j]);
}
if(i<=w){
continue;
}
int x=i-w-1,head=0,tail=0;
for(int j=0;j<=mp;j++){
while(head<tail&&deq[head]<j-lb[i]){
head++;
}
while(head<tail&&dp[x][deq[tail-1]]+deq[tail-1]*vb[i]<=dp[x][j]+j*vb[i]){
tail--;
}
deq[tail++]=j;
if(head<tail){
dp[i][j]=max(dp[i][j],dp[x][deq[head]]+deq[head]*vb[i]-j*vb[i]);
}
}
head=tail=0;
for(int j=mp;j>=0;j--){
while(head<tail&&deq[head]>j+ls[i]){
head++;
}
while(head<tail&&dp[x][deq[tail-1]]+deq[tail-1]*vs[i]<=dp[x][j]+j*vs[i]){
tail--;
}
deq[tail++]=j;
if(head<tail){
dp[i][j]=max(dp[i][j],dp[x][deq[head]]+deq[head]*vs[i]-j*vs[i]);
}
}
}
cout<<dp[t][0]<<endl;
return 0;
}