Gym 100829S_surf 动态规划的优化

题目大意是,非你若干个任务,任务分别对应开始时间、预期收益、持续时间三项指标,让你从中选择一个受益最大的方案(没有开始时间相同的任务)。

于是,标准状态转移方程应当为,设DP[K]为选择了前K个任务的最大收益,后面转移为DP[K+1]=MAX且能够共存的(DP[I]);很容易想到N^2的暴力更新,但是这题数量太大,会炸得连渣都不剩。于是需要优化到较低的数量级(比如NLOGN)

注意到,我们也许不用对某个任务来选取前K个的最大值,不容易想到优化但是想想刘汝佳同志的话——不方便直接求解的时候想想更新状态看看,于是就变成了状态更新公式而不是状态转移方程。——对于求得的DP[K]更新所有合法 的,大于K的DP值。注意到,当前序列已经经过了排序,所以,当找到第一个合法的DP[P]P>K时候,就会有P+1也是合法的状态,因此很容易想到树状数组相对最大值+二分查找确定位置(只要有比较函数就可以写二分)。最后复杂度是NLOGN。

另外根据某些奇怪的树上的书法,,任何一个动态规划的优化算法都应当从如下三个状态进行考虑:
1、状态总数        N    N

2、决策数         N    1

3、状态转移时间复杂度   1    LOGN

分别对应原始DP和优化后的DP

AC代码如下:考虑到没有校园网所以在VJ上面交的。。。于是投篮用了万能头文件,好孩子不要学我哟~

#include<bits/stdc++.h>
using namespace std;

const long long MAXN=300233;

class Mession
{
    public:
        long long a,b,p;
};Mession messions[MAXN];
long long tree[MAXN];
long long dp[MAXN];
long long n;
void insert(int pos,long long key)
{
    while(pos<=n)
    {
        tree[pos]=max(key,tree[pos]);
        pos+=pos&(-pos);
    }
}
long long getSum(int pos)
{
    long long ans=0;
    while(pos)
    {
        ans=max(tree[pos],ans);
        pos-=pos&(-pos);
    }return ans;
}
bool cmp(Mession m1,Mession m2)
{
    return m1.a<m2.a;
}
void init()
{
cin>>n;
    for(int i=1;i<=n;++i)
    {
        cin>>messions[i].a>>messions[i].p>>messions[i].b;
        messions[i].b+=messions[i].a;
    }sort(messions+1,messions+n+1,cmp);
    long long ans=0;
    for(int i=1;i<=n;++i)
    {
        dp[i]=messions[i].p;
        dp[i]+=getSum(i);
        Mession mm;mm.a=messions[i].b;
        int pos=lower_bound(messions+1,messions+1+n,mm,cmp)-messions;
        insert(pos,dp[i]);
        ans=max(dp[i],ans);
    }
    cout<<ans<<endl;
}


int main()
{
    cin.sync_with_stdio(false);
    init();    
    return 0;
}
原文地址:https://www.cnblogs.com/rikka/p/7487097.html