Trade
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3588 Accepted Submission(s): 1161
Problem Description
Recently, lxhgww is addicted to stock, he finds some regular patterns after a few days' study.
He forecasts the next T days' stock market. On the i'th day, you can buy one stock with the price APi or sell one stock to get BPi.
There are some other limits, one can buy at most ASi stocks on the i'th day and at most sell BSi stocks.
Two trading days should have a interval of more than W days. That is to say, suppose you traded (any buy or sell stocks is regarded as a trade)on the i'th day, the next trading day must be on the (i+W+1)th day or later.
What's more, one can own no more than MaxP stocks at any time.
Before the first day, lxhgww already has infinitely money but no stocks, of course he wants to earn as much money as possible from the stock market. So the question comes, how much at most can he earn?
He forecasts the next T days' stock market. On the i'th day, you can buy one stock with the price APi or sell one stock to get BPi.
There are some other limits, one can buy at most ASi stocks on the i'th day and at most sell BSi stocks.
Two trading days should have a interval of more than W days. That is to say, suppose you traded (any buy or sell stocks is regarded as a trade)on the i'th day, the next trading day must be on the (i+W+1)th day or later.
What's more, one can own no more than MaxP stocks at any time.
Before the first day, lxhgww already has infinitely money but no stocks, of course he wants to earn as much money as possible from the stock market. So the question comes, how much at most can he earn?
Input
The first line is an integer t, the case number.
The first line of each case are three integers T , MaxP , W .
(0 <= W < T <= 2000, 1 <= MaxP <= 2000) .
The next T lines each has four integers APi,BPi,ASi,BSi( 1<=BPi<=APi<=1000,1<=ASi,BSi<=MaxP), which are mentioned above.
The first line of each case are three integers T , MaxP , W .
(0 <= W < T <= 2000, 1 <= MaxP <= 2000) .
The next T lines each has four integers APi,BPi,ASi,BSi( 1<=BPi<=APi<=1000,1<=ASi,BSi<=MaxP), which are mentioned above.
Output
The most money lxhgww can earn.
Sample Input
1 5 2 0 2 1 1 1 2 1 1 1 3 2 1 1 4 3 1 1 5 4 1 1
Sample Output
3
Author
lxhgww
Source
提交情况:WA WA WA WA WA WA WA WA WA WA WA ....AC
题目大意:
有个人买卖股票来赚钱,
给出一个交易区段的天数t , 有一个可以购买的股票数量上限maxp,并且当前交易与上一次交易必须
间隔W天
接着会给出每天的股票的买入卖出价格ap[i],bp[i] , 接着每天买入卖出的股票数量限制as[i],bs[i]。
求到了第t天可以获得的最大利润。
解题思路:
总的思路是
前W+1天【1,W+1】,只能够进行两种状态转移:j>0时从前一天直接转移,不买不卖;还有从前一天的j==0转移到今天的j>0,
也就是今天第一次买股票,买了j支。
剩余的时间,除了以上两种状态转移之外,还可以有从第i-W-1天的已有的j-k支股票的前提下,再买入k支股票。
此外还需要注意初始化的问题,
(1)一开始做的时候将dp全都初始化为了0,这样的话结果一定是错的,因为dp小于0的时候卖出股票之后收益可能会比dp=0的时候要大。
正确的做法应该是赋值为无穷小。
(2)还有,dp[1][0]必然就是0的了,前W+1天的dp[i][0]都是0,因为绝对不可能进行卖出交易。而后面的dp[i][0]就不一定是零,可能是刚刚把股票卖完了。
反省一下,wa了这么多次,主要还是因为自己的下标处理能力太差。出现的错误有head和tail的初始化写反了,还有上面说的初始化的问题,
还有后来优先队列删除末尾节点的地方把tail写成了head。还有一个自觉得很重要的错误,在第二份ac代码中会有标出。
第一遍先用普通朴素的dp做了一遍,T了
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <numeric>
#include <functional>
#define maxn 2015
using namespace std;
int ap[maxn],bp[maxn],as[maxn],bs[maxn];
int dp[maxn][maxn];
int n,maxp,W,ans;
int main()
{
int t;
scanf("%d",&t);
while(t--){
memset(dp,0,sizeof(dp));
ans=0;
scanf("%d %d %d",&n,&maxp,&W);
for(int i=1;i<=n;i+=1){
scanf("%d %d %d %d",&ap[i],&bp[i],&as[i],&bs[i]);
}
for(int i=1;i<=maxp;i+=1){
dp[1][i]=dp[1][i-1]-ap[i];
}
for(int i=2;i<=n;i+=1){
for(int j=0;j<=maxp;j+=1){
dp[i][j]=max(dp[i-1][0]-j*ap[i],dp[i-1][j]);
if(i>W+1){
//dp[i][j]=max(dp[i-1][0]-j*ap[i],dp[i-1][j]);
for(int k=1;k<=j&&k<=as[i];k+=1){ /*买进*/
//for(int k=1;j+k<=maxp&&k<=as[i];k+=1){
dp[i][j]=max(dp[i][j],dp[i-W-1][j-k]-ap[i]*k);
//dp[i][j]=max(dp[i][j],dp[i-W-1][j+k]-ap[i]*k);
}
for(int k=1;j+k<=maxp&&k<=bs[i];k+=1){ /*卖出*/
//for(int k=1;k<=j&&k<=bs[i];k+=1){
dp[i][j]=max(dp[i][j],dp[i-W-1][j+k]+bp[i]*k);
//dp[i][j]=max(dp[i][j],dp[i-W-1][j-k]+bp[i]*k);
}
}
//else{
//dp[i][j]=dp[i-1][j];
//dp[i][j]=max(dp[i-1][j],dp[i-1][0]-j*ap[i]);
//}
if(i==n){
ans=max(ans,dp[i][j]);
}
}
}
printf("%d\n",/*dp[n][0]*/ans);
}
return 0;
}
第二遍,用单调队列优化,wa了很多,但最后过了(下面是注释版和简明版两个版本):
<pre name="code" class="cpp">#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <numeric>
#include <functional>
#define maxn 2052
using namespace std;
int ap[maxn],bp[maxn],as[maxn],bs[maxn];
int dp[maxn][maxn];
int q[maxn],J[maxn];
int n,maxp,W,ans;
int main()
{
int t;
scanf("%d",&t);
while(t--){
memset(dp,128,sizeof(dp));
//原来错了,因为没有初始化无穷小,这里也导致了8到3的蜕变
//想想在i=n-1的某种情况下,从q里取出的值暂时是负的,
//如果初始化为0这个时候就就不会选择负的数了
ans=0;
scanf("%d %d %d",&n,&maxp,&W);
for(int i=1;i<=n;i+=1){
scanf("%d %d %d %d",&ap[i],&bp[i],&as[i],&bs[i]);
}
/*dp[1][0]=0;
for(int i=1;i<=maxp;i+=1){
dp[1][i]=dp[1][i-1]-ap[1];
}*/
dp[0][0]=0;
for(int i=1;i<=W+1;i+=1){
for(int j=0;j<=maxp&&j<=as[i];j+=1){ //因为是前W+1天只能买一次,所以必须加上一个j<=as[i]的限制
dp[i][j]=-j*ap[i];
}
}
for(int i=1;i<=n;i+=1){
for(int j=0;/*i>=W+2&&*/j<=maxp;j+=1){
//这里不能有注释掉的语句,因为虽然当天不能卖那么多的股票,但是可以从昨天转移过来
dp[i][j]=max(dp[i][j],dp[i-1][j]);
}
if(i<W+2) continue; //因为这里没有continue会re
int tail=0,head=1; //这里有错原来是 int tail=1,head=0; 发生了从8到3的蜕变
for(int j=0;j<=maxp;j+=1){ /*买进*/
while(/*j-J[head]<as[i]*/J[head] + as[i] < j&&head<=tail) head+=1; //维护单调队列的长度
int newdp=dp[i-W-1][j]+j*ap[i];
while(head<=tail&&newdp>q[tail]) tail-=1;
tail+=1;
q[tail]=newdp,J[tail]=j;
//j[tail]这一步很重要,因为是多重背包,每一次入货出货都有限制,
//而由于使用单调队列取得的最优解不一定符合限制条件,会过时
//while(/*j-J[head]<as[i]*/J[head] + as[i] < j&&head<=tail) head+=1; //维护单调队列的长度
if(head<=tail) dp[i][j]=max(dp[i][j],q[head]-j*ap[i]);
}
tail=0,head=1;//这里有错原来是 int tail=1,head=0;
for(int j=maxp;j>=0;j-=1){ /*卖出*/
while(/*J[head]-j<bs[i]*/J[head] - bs[i] > j&&head<=tail) head+=1; //维护单调队列的长度
int newdp=dp[i-W-1][j]+j*bp[i];
while(head<=tail&&newdp>q[tail]) tail-=1;
tail+=1;
q[tail]=newdp,J[tail]=j;
//j[tail]这一步很重要,因为是多重背包,每一次入货出货都有限制,
//而由于使用单调队列取得的最优解不一定符合限制条件,会过时
//while(/*J[head]-j<bs[i]*/J[head] - bs[i] > j&&head<=tail) head+=1; //维护单调队列的长度
if(head<=tail) dp[i][j]=max(dp[i][j],q[head]-j*bp[i]);
}
if(i==n){
for(int j=0;j<=maxp;j+=1){
ans=max(ans,dp[n][j]);
}
}
}
printf("%d\n",/*dp[n][0]*/ans);
}
return 0;
}
简明版
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <numeric>
#include <functional>
#define maxn 2052
using namespace std;
int ap[maxn],bp[maxn],as[maxn],bs[maxn];
int dp[maxn][maxn];
int q[maxn],j[maxn];
int n,maxp,w,ans;
int main()
{
int t;
scanf("%d",&t);
while(t--){
memset(dp,128,sizeof(dp));
ans=0;
scanf("%d %d %d",&n,&maxp,&w);
for(int i=1;i<=n;i+=1){
scanf("%d %d %d %d",&ap[i],&bp[i],&as[i],&bs[i]);
}
dp[0][0]=0;
for(int i=1;i<=w+1;i+=1){
for(int j=0;j<=maxp&&j<=as[i];j+=1){
dp[i][j]=-j*ap[i];
}
}
for(int i=1;i<=n;i+=1){
for(int j=0;j<=maxp;j+=1){
dp[i][j]=max(dp[i][j],dp[i-1][j]);
}
if(i<w+2) continue;
int tail=0,head=1;
for(int j=0;j<=maxp;j+=1){
while(j[head] + as[i] < j&&head<=tail) head+=1;
int newdp=dp[i-w-1][j]+j*ap[i];
while(head<=tail&&newdp>q[tail]) tail-=1;
tail+=1;
q[tail]=newdp,j[tail]=j;
if(head<=tail) dp[i][j]=max(dp[i][j],q[head]-j*ap[i]);
}
tail=0,head=1;
for(int j=maxp;j>=0;j-=1){
while(j[head] - bs[i] > j&&head<=tail) head+=1;
int newdp=dp[i-w-1][j]+j*bp[i];
while(head<=tail&&newdp>q[tail]) tail-=1;
tail+=1;
q[tail]=newdp,j[tail]=j;
if(head<=tail) dp[i][j]=max(dp[i][j],q[head]-j*bp[i]);
}
if(i==n){
for(int j=0;j<=maxp;j+=1){
ans=max(ans,dp[n][j]);
}
}
}
printf("%d\n",/*dp[n][0]*/ans);
}
return 0;
}