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;

}

 

 

你可能感兴趣的:(线段树)