P2569 [SCOI2010] 股票交易
我记得有这么一个结论:一次性买完或者一次性卖完是最优的,但是在这道题里似乎不适用。
我们看,数据范围只有 \(2000\),直接想到设 \(dp_{i,j}\) 表示如果在第 \(i\) 天有 \(j\) 支股票的最大收益。那么转移方程就有:
\[dp_{i,j} = \max\begin{cases}
dp_{i - 1,j} & \text{不买} \\
-AP_i \times j & \text{凭空买} \\
dp_{i - w - 1,k} - (j - k) \times AP_i & \text{在之前的基础上买} \\
dp_{i - w - 1,j} + (k - j) \times BP_i & \text{在之间的基础上卖}
\end{cases}
\]
然后来看看复杂度,设 \(\text{MaxP}\) 和 \(n\) 同阶。枚举状态 \(\mathcal{O}(n^2)\),转移 \(\mathcal{O}(n)\),合起来 \(\mathcal{O}(n^3)\),不太行啊。我们直接单调队列优化第 \(3,4\) 个转移方程,这道题就做完了。
code:
#include<bits/extc++.h>
using namespace std;
const int maxn = 2005;
int t,mxp,w;
int ap[maxn],bp[maxn],as[maxn],bs[maxn];
int dp[maxn][2005];
deque<int>q;
signed main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> t >> mxp >> w;for (int i = 1; i <= t; i++)cin >> ap[i] >> bp[i] >> as[i] >> bs[i];memset(dp,~0x3f,sizeof dp);for (int i = 1; i <= t; i++){for (int j = 0; j <= as[i]; j++)dp[i][j] = -j * ap[i];for (int j = 0; j <= mxp; j++)dp[i][j] = max(dp[i][j],dp[i - 1][j]);if (i <= w)continue;q.clear();for (int j = 0; j <= mxp; j++){while (!q.empty() && q.front() < j - as[i])q.pop_front();while (!q.empty() && dp[i - w - 1][q.back()] + q.back() * ap[i] <= dp[i - w - 1][j] + j * ap[i])q.pop_back();if (!q.empty())dp[i][j] = max(dp[i][j],dp[i - w - 1][q.front()] + q.front() * ap[i] - j * ap[i]);q.push_back(j);}q.clear();for (int j = mxp; j >= 0; j--){while (!q.empty() && q.front() > j + bs[i])q.pop_front();while (!q.empty() && dp[i - w - 1][q.back()] + q.back() * bp[i] <= dp[i - w - 1][j] + j * bp[i])q.pop_back();if (!q.empty())dp[i][j] = max(dp[i][j],dp[i - w - 1][q.front()] + q.front() * bp[i] - j * bp[i]);q.push_back(j);}}int ans = 0;for (int i = 0; i <= mxp; i++)ans = max(ans,dp[t][i]);cout << ans;return 0;
}