zoj3381 Osaisen Choudai!
题意:忽略题目背景,就是要收集最多的钱, 如果第i天拿到了si 的钱, 那么第i+x[i] 天 到 第i + y[i] - 1 天必须再拿一次,否则就再也拿不到钱了,当然,第i +x[i]天之前也是拿不到的, 题目要求第一天必须拿。。
分析:一开始想到了用记忆化搜索,代码很短, 一下就敲完了, 结果也果断超时了
后来想到了按记忆化搜索的思路,直接从最后一天开始算起,用dp[i] 表示第i天拿到了钱之后,到第n天为止,最多拿到的钱数,
那么dp[i] = s[i] + max(dp[j]) (i + x[i] <= j <= i + y[i] - 1)
到这一步就很明显了,这里涉及到了区间最值的问题,我们可以用线段树来维护
zoj3381
#include<iostream> #include<algorithm> #include<stdio.h> #include<string.h> #include<stdlib.h> using namespace std; const int N = 50000 + 10; int dp[N]; int s[N], x[N], y[N]; int n; struct node { int l, r, maxx; }p[N * 3]; void build(int s, int t, int k) { p[k].l = s, p[k].r = t; p[k].maxx = 0; if( s == t ) return ; int kl = k << 1, kr = kl + 1, mid = (s + t) >> 1; build(s, mid, kl); build(mid + 1, t, kr); } void update(int k, int s, int val) { if(s == p[k].l && p[k].r == p[k].l) { p[k].maxx = val; return ; } int kl = k << 1, kr = kl + 1, mid = (p[k].l + p[k].r) >> 1; if( s <= mid ) update(kl, s, val); else update(kr, s, val); p[k].maxx = max(p[kl].maxx, p[kr].maxx); } int query(int s, int t, int k) { if( s <= p[k].l && t >= p[k].r) { return p[k].maxx; } int mid = (p[k].l + p[k].r) >> 1; int kl = k << 1, kr = kl + 1; int a = 0, b = 0; if(s <= mid) a = query(s, t, kl); if(t > mid) b = query(s, t, kr); return max(a, b); } int main() { while(scanf("%d",&n) == 1) { for(int i = 1; i <= n; ++i) scanf("%d %d %d",&s[i], &x[i], &y[i]); build(1, n, 1); update(1, n, s[n]); int ans = 0; for(int i = n - 1; i >= 1; --i) { int l = i + x[i]; int r = i + y[i] - 1; r = min(r, n); int val = s[i] + query(l, r, 1); update(1, i, val); if(i == 1) ans = val; } printf("%d\n",ans); } return 0; }