zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    Flash请求不能传Cookie的PHP解决方案
    免费与不免费
    js编码之 encodeURIComponent
    锁定老帖子 主题:你应当了解的几个CSS3新技术
    清除浮动mini版
    iPad网页开发教程及规则
    手持设备meta设置
    使用 jQuery 在新窗口打开外部链接
    jQuery GET POST AJAX與php异步加载
    游戏英文词汇命名——备用
  • 原文地址:https://www.cnblogs.com/gabo/p/2456991.html
Copyright © 2011-2022 走看看