P10655 [ROI 2017] 反物质 (Day 2) 题解
P10655 [ROI 2017] 反物质 (Day 2) 题解
知识点
动态规划,记忆化搜索,单调栈,单调队列,决策单调性。
分析
我们考虑设 \(f_i\) 表示质量为 \(i\) 时,最坏情况下利润最大值,然后正序 DP,发现不太好做。
对于这类「最坏至少」问题,我们可以考虑用记忆化搜索的思路来转移,然后在这题里就会得到倒序 DP 的想法。
于是有了:设 \(f_i\) 表示质量为 \(i\) 时,之后搜索下去最坏至少有多少利润。
\[\begin{aligned}
f_i &= \max_{(l,r,c)} [i+r \le m] \min_{k=l}^{r} (f_{i+k} + 10^9k - c) \\
f_i + 10^9i &= \max_{(l,r,c)} [i+r \le m] \min_{k=l}^{r} [f_{i+k} + 10^9(i+k) - c] \\
\end{aligned}
\]
设 \(f'_i = f_i +10^9i\),式子就可以化简为:
\[\begin{aligned}
f'_i &= \max_{(l,r,c)} [i+r \le m] \min_{k=l}^{r} [f'_{i+k} - c] \\
\end{aligned}
\]
而初始状态就是 \(f'_i = [i\le m]10^9i\)。
实现
单调队列
于是我们单调队列就有了时空皆是 \(O(nm)\) 的做法。
单调栈
如果把空间复杂度限制在 \(O(n+m)\) 以内呢?
其实发现我们的单调队列所对应的序列都是重复的,将其中某个元素 \(i\) 从单调队列中踢出的另一个元素 \(pre_i\) 也是相同的。
我们可以在 DP 的同时同单调栈处理出 \(pre_i\),然后用 \(n\) 个值记录合法最值位置就可以模拟 \(n\) 个单调队列。
时间复杂度同样是 \(O(nm)\) 的,但是空间复杂度骤降为 \(O(n+m)\)。
代码
单调队列
#define Plus_Cat "anti"
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
#define VAL 1000000000ll
#define ll long long
#define RCL(a,b,c,d) memset(a,b,sizeof(c)*(d))
#define FOR(i,a,b) for(int i(a);i<=(int)(b);++i)
#define DOR(i,a,b) for(int i(a);i>=(int)(b);--i)
#define tomax(a,...) ((a)=max({(a),__VA_ARGS__}))
#define tomin(a,...) ((a)=min({(a),__VA_ARGS__}))
#define EDGE(g,i,x,y) for(int i=(g).h[(x)],y=(g)[(i)].v;~i;y=(g)[(i=(g)[i].nxt)>0?i:0].v)
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);return Main();}signed Main
using namespace std;
constexpr int N(100+10),M(2e6+10);
namespace IOEcat {
//Fast IO
} using namespace IOEcat;
int n,m;
int l[N],r[N],c[N],cur[N];
ll ans;
ll f[M];
deque<int> DQ[N];
signed main() {
#ifdef Plus_Cat
freopen(Plus_Cat ".in","r",stdin),freopen(Plus_Cat ".out","w",stdout);
#endif
I(n,m);
FOR(i,1,n)I(l[i],r[i],c[i]),cur[i]=m;
DOR(i,m,0) {
f[i]=VAL*i;
FOR(j,1,n)if(i+r[j]<=m) {
deque<int> &dq(DQ[j]);
while(cur[j]>=i+l[j]) {
int now(cur[j]--);
while(!dq.empty()&&f[dq.back()]>=f[now])dq.pop_back();
dq.push_back(now);
}
while(dq.front()>i+r[j])dq.pop_front();
tomax(f[i],f[dq.front()]-c[j]);
}
}
O(f[0],'\n');
return 0;
}
单调栈
#define Plus_Cat "anti"
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
#define VAL 1000000000ll
#define ll long long
#define RCL(a,b,c,d) memset(a,b,sizeof(c)*(d))
#define FOR(i,a,b) for(int i(a);i<=(int)(b);++i)
#define DOR(i,a,b) for(int i(a);i>=(int)(b);--i)
#define tomax(a,...) ((a)=max({(a),__VA_ARGS__}))
#define tomin(a,...) ((a)=min({(a),__VA_ARGS__}))
#define EDGE(g,i,x,y) for(int i=(g).h[(x)],y=(g)[(i)].v;~i;y=(g)[(i=(g)[i].nxt)>0?i:0].v)
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);return Main();}signed Main
using namespace std;
constexpr int N(100+10),M(2e6+10);
int n,m,top;
int l[N],r[N],c[N],st[M],cur[N],pre[M];
ll f[M];
signed main() {
#ifdef Plus_Cat
freopen(Plus_Cat ".in","r",stdin),freopen(Plus_Cat ".out","w",stdout);
#endif
cin>>n>>m;
FOR(i,1,n)cin>>l[i]>>r[i]>>c[i],cur[i]=m+1;
DOR(i,m,0) {
f[i]=VAL*i;
FOR(j,1,n) {
tomin(cur[j],i+r[j]);
while(pre[cur[j]]>=i+l[j])cur[j]=pre[cur[j]];
tomax(f[i],f[cur[j]]-c[j]);
}
while(top&&f[st[top]]>f[i])pre[st[top--]]=i;
st[++top]=i;
}
cout<<f[0]<<endl;
return 0;
}

浙公网安备 33010602011771号