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;
}

 

posted @ 2013-04-06 15:48  枕边梦  阅读(263)  评论(0编辑  收藏  举报