• SPOJ RENT 01背包的活用+二分


    这个题目给定N航班的发出时间和结束时间以及价值,要求不冲突时间的最大价值

    第一时间想到经典的N方DP,即对航班按发出时间排一下序之后每个i对前面的都扫一遍

    时间过不了N有10万,只能想优化了,一开始想了个用树状数组记录每次加入某个航班之后 后面的所有在结束时间后的区间全部可以加一个最大值,但是首先时间区间的量更大,而且树状数组不好这么操作

    后来实在没见过这种DP,还是看的别人的算法,还是挺叼的,不过其实还是沿用的背包思想

    对于每个航班区间,我只需要考虑取或者不取,这样的话,对航班按出发时间S排序,从后往前,对该航班结束时间T找到 i+1到n中满足出发时间Ti<=Sk(i<k<=n)的,由于找到的已经是最优值的,所以直接+现在的价值,当然,如果不取该区间的话 dp[i]=dp[i+1]即可.用了典型的01背包思想。因为查找可以用二分,所以把时间复杂度控制在NlogN,不会超时

    当然,为什么要从后往前扫值得深思,首先这样扫过来肯定是对的,因为他的子结构只会在他后面(即满足结束时间《=发出时间的),而且他们已经是最优了。反过来如果从前面开始扫,那他们的子结构还是在后面,但后面的还没访问过,不是最优值,所以无法转移。因此这就是为什么转移是这样的

    这个题目真的不错,DP模型很经典,但是状态转移很新颖

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    struct node
    {
        int s,d,p;
        bool operator < (const node& rhs) const{
            return s<rhs.s;
        }
    }rent[10010];
    const int N = 10010;
    int dp[N];
    int bs(int v,int L,int R)
    {
        int mid;
        while (L<R){
           mid=(L+R)>>1;
           if (v<=rent[mid].s) R=mid;
           else  L=mid+1;
        }
        return L;
    }
    int main()
    {
        int t;
        scanf("%d",&t);
        while(t--)
        {
            memset(dp,0,sizeof dp);
            int n;
            scanf("%d",&n);
            for (int i=0;i<n;i++){
                scanf("%d%d%d",&rent[i].s,&rent[i].d,&rent[i].p);
                rent[i].d=rent[i].s+rent[i].d;
            }
            sort(rent,rent+n);
            memset(dp,0,sizeof dp);
            for (int i=n-1;i>=0;i--){
                dp[i]=dp[i+1];
                int loc=bs(rent[i].d,i+1,n);
                dp[i]=max(dp[i],dp[loc]+rent[i].p);
            }
            printf("%d
    ",dp[0]);
        }
        return 0;
    }
    

      

  • 相关阅读:
    LIS
    原根
    数三角形
    组合数问题
    最短路问题
    2020总结
    树状数组
    康托展开
    LCA
    并查集
  • 原文地址:https://www.cnblogs.com/kkrisen/p/3874369.html
Copyright © 2020-2023  润新知