题解 糖

传送门

首先有一个 \(O(nc^2)\) 的暴力
\(f_{i, j}\) 为考虑到第 \(i\) 个位置,当前有 \(j\) 个糖的最大收益
转移的时候枚举在当前位置买入/卖出几块糖

这个暴力可以优化到 \(O(nc)\)
具体地,可以利用完全背包的思想,以卖出为例

\[f_{i, j}=max\{f_{i, j}, f_{i, j+1}+sell_i\} \]

\[f_{i, j}=f_{i-1, j-cost_{i, i+1}} \]

然后可以发现这个 \(f_i(j)\) 是一个凸函数
结合转移式,发现转移实际上是在给原凸包与原凸包上每个点上延伸出来的斜率为 \(-sell_i\) 的直线取max
于是发现实际上斜率小于 \(-sell_i\) 的拐点都没用了
另一个转移也同理
于是可以用单调队列维护这个凸包
原序列上的每个位置最多给原序列添加两个拐点,于是复杂度是 \(O(n)\)
细节很多,我把buy和sell弄反了调了两个小时

Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n;
ll a[N], sell[N], buy[N], cost[N], c;

namespace force{
	ll dp[1010][1010];
	void solve() {
		memset(dp, 128, sizeof(dp));
		for (int i=0; i<=c; ++i) dp[n][i]=0;
		for (int i=n-1; ~i; --i) {
			for (int j=0; j<=c; ++j) {
				for (int k=0; k<=j-cost[i]; ++k) {
					dp[i][j]=max(dp[i][j], dp[i+1][k]+(j-cost[i]-k)*sell[i]);
				}
				// dp[i][j]=max(dp[i][j], dp[i+1][c-cost[i]]-(c-j)*buy[i]);
				for (int k=0; k<=c-j; ++k) {
					dp[i][j]=max(dp[i][j], dp[i+1][j+k-cost[i]]-k*buy[i]);
				}
			}
		}
		printf("%lld\n", -dp[0][0]);
		exit(0);
	}
}

signed main()
{
	freopen("candy.in", "r", stdin);
	freopen("candy.out", "w", stdout);

	n=read(); c=read();
	for (int i=1; i<=n; ++i) a[i]=read();
	for (int i=0; i<n; ++i) cost[i]=a[i+1]-a[i];
	for (int i=0; i<n; ++i) buy[i]=read(), sell[i]=read();
	force::solve();

	return 0;
}
posted @ 2021-11-11 21:07  Administrator-09  阅读(0)  评论(0编辑  收藏  举报