fafu 1231 dp(线段树优化dp)

fafu 1231 dp(线段树优化dp)

http://acm.fafu.edu.cn/problem.php?id=1231 

 

这题是说在 n 个时间单位内工作产生不同的价值
但在每个单位时间工作后都有限定接下去几个单位时间不能再工作
和 接下去几个单位时间内必须要再次工作

这题可以从前往后推也可以从后往前推,但从前往后推必须
两重循环(我还不会优化这种情况)

从前往后推,TLE
//fafu 1231 

//这个代码是从前往后推(很暴力),会TLE的

#include <stdio.h>
#include <string.h>

#define N 50005

int n;
int dp[N], val[N], no_work[N], work[N];

int max(int a, int b)
{
    return a > b ? a : b;
}

int main()
{
    freopen("in.txt", "r", stdin);
    int n_case;
    scanf("%d", &n_case);
    while(n_case--)
    {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
            scanf("%d%d%d", &val[i], &no_work[i], &work[i]);

        int max = 0;
        for(int i = 1; i <= n; ++i)//从前往后推
        {
            dp[i] = val[i];
            //第i 个时间单位要工作,就要 在i 之前的单位时间
            //里寻找 单位时间j 工作的最大价值加上 i 的价值
            //才能使得第 i 个时间单位工作后得到的价值最大
            for(int j = i; j > 0; --j)
            {   //若j 工作后,i刚好在 j限定的不能工作和必须工作之间,并且j 时
                //的价值加上i 的价值比 i 目前的价值大
                if(i-j >= no_work[j] && i-j < work[j] && dp[i] < dp[j] + val[i])
                {
                    dp[i] = dp[j] + val[i];
                    if(max < dp[i])
                        max = dp[i];
                }
            }
        }

        printf("%d\n", max);
    }
    return 0;
}

 

从后往前推(TLE)
//fafu 1231

//从后往前推,稍微优化,还是TLE
//不过有些人还是AC 了

#include <stdio.h>
#include <string.h>

#define N 50005

int n;
int dp[N], val[N], no_work[N], work[N], tree[N*4];

int max(int a, int b)
{
    return a > b ? a : b;
}


int main()
{
    int n_case;
    scanf("%d", &n_case);
    while(n_case--)
    {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
            scanf("%d%d%d", &val[i], &no_work[i], &work[i]);

        int tmax, m = 0;
        for(int i = n; i > 0; --i)  //从后往前推
        {
            dp[i] = val[i];
            tmax = 0;
            //若第i 个单位时间要工作,则找 i 后第几个时间单位 第几个单位
            //时间继续工作 能得到最大价值
            for(int j = i+no_work[i]; j <= n && j < i+work[i]; ++j)
            {
                tmax = max(tmax, dp[j]);
            }
            dp[i] += tmax;  //更新 最大价值
            if(m < dp[i])
                m = dp[i];
        }
        printf("%d\n", m);
    }
    return 0;
}

 

线段树优化
//fafu 1231 dp(线段树优化dp)

//具体看一下代码

#include <stdio.h>
#include <string.h>

#define N 50005

int n;
int dp[N], val[N], no_work[N], work[N], tree[N*4];

int max(int a, int b)
{
    return a > b ? a : b;
}

void build_tree(int l, int r, int root)
{
    if(l == r)
    {
        tree[root] = val[l];
        return;
    }
    int mid = (l + r) >> 1;
    build_tree(l, mid, root * 2);
    build_tree(mid + 1, r, root * 2 + 1);
    tree[root] = max(tree[root*2], tree[root*2+1]);
}


//询问,返回最大值
int query(int l, int r, int ll, int rr, int root)
{
    if(l == r || (l == ll && r == rr))
        return tree[root];

    int mid = (l + r) >> 1;

    if(mid < ll)
        return query(mid+1, r, ll, rr, root*2+1);
    else if(mid >= rr)
        return query(l, mid, ll, rr, root*2);
    else
        return max(query(l, mid, ll, mid, root*2), query(mid+1, r, mid+1, rr, root*2+1));
}

void update(int l, int r, int ll, int root)
{
    if(l == r)
    {
        tree[root] = dp[l];
        return;
    }
    int mid = (l + r) >> 1;
    if(mid < ll)
        update(mid+1, r, ll, root*2+1);
    else if(mid >= ll)
        update(l, mid, ll, root*2);
    tree[root] = max(tree[root*2], tree[root*2+1]);

}

int main()
{
    int n_case;
    scanf("%d", &n_case);
    while(n_case--)
    {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
            scanf("%d%d%d", &val[i], &no_work[i], &work[i]);
        build_tree(1, n, 1);    //建树,根节点保持最大值

        for(int i = n; i > 0; --i)//从后往前推
        {
            dp[i] = val[i];
            int tmp = i + work[i] - 1;  //tmp单位时间内必须再次工作
            if(tmp > n) //若tmp超过 最大单位时间则等于最大单位时间
                tmp = n;
            //i+no_work[i]-1 表示这时间内不能再工作
            //这里要注意 这个时间要比 tmp 早
            if(i + no_work[i] -1 < tmp && tmp != i)
            {   //若i 要工作,找i 之后不能工作 和 必须工作 这
                //两段时间内价值最大的 单位时间 作为 i 的下一个工作单位时间
                dp[i] += query(1, n, i+no_work[i], tmp, 1);
                update(1, n, i, 1); //更新根节点
            }

        }
        printf("%d\n", tree[1]);
    }
    return 0;
}

 

树状数组优化
////fafu 1231 dp(树状数组优化dp)

#include <stdio.h>
#include <string.h>

#define N 50005

int n;
int tree[N], val[N], no_work[N], work[N];

int max(int a, int b)
{
    return a > b ? a : b;
}

int lowbit(int root)    //求二进制最低位的 1
{
    return root & (root^(root-1));  //等价于 root & (-root),不知道为什么
}

void build_tree(int root)
{
    int d = root - lowbit(root), m = val[root];
    for(int i = d+1; i < root; ++i)
        m = max(m, val[i]);
    tree[root] = m;
}

int find_max(int l, int r) //找段内最大值
{
    int ans = val[r];
    while(1)
    {
        ans = max(ans, val[r]);
        if(l == r)
            break;
        //for 的意思是:若 r-l >= lowbit(r) 表示 tree[r]保存是从 r-lowbit(r)+1 到tree[r]
        //的最大值而 l < r-lowbit(r) +1,说明还要比较从 l到 r-lowbit(r)的最大值
        //若 r-l >= lowbit(r)不成立则 r-lowbit(r)+1 小于l,因此先比较
        //单个数的值(val[r]) 和 ans(记录最大值) 的大小,即ans = max(val[r], num[r])
        //然后在比较 l 到 r-1 的最大值(即以下这个for)
        for(r -= 1; r - l >= lowbit(r); r -= lowbit(r))
        {
            ans = max(ans, tree[r]);
        }
    }
    return ans;
}

void update(int root)
{   //和 root最低位1 以下的位为都为0 一样的数保存
    //的最大值范围都包括 val[root]
    int tmp = val[root];//先取最低位1 的位置
    while(root <= n)
    {
        tree[root] = max(tree[root], tmp);
        root += lowbit(root);   //相当于把最低位1 加1 变成0,进1位(相当于在后面加0)
    }
}


int main()
{    
    int n_case;
    scanf("%d", &n_case);
    while(n_case--)
    {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
        {
            scanf("%d%d%d", &val[i], &no_work[i], &work[i]);
            build_tree(i);
        }
        int ans = 0;
        for(int i = n; i > 0; --i)
        {
            int tmp = i + work[i] - 1;  //记录那个单位时间必须工作
            if(tmp > n)     //若大于 最大时间单位,则令其等于最大单位时间
                tmp = n;
            if(i + no_work[i] <= tmp)
            {
                val[i] += find_max(i+no_work[i], tmp);
                update(i);
            }
            ans = max(ans, val[i]);
        }
        printf("%d\n", ans);
    }
    return 0;
}

 

 

posted @ 2012-04-19 13:51  gabo  阅读(327)  评论(0编辑  收藏  举报