《算法竞赛进阶指南》0x59单调队列优化DP POJ1821 围栏

题目链接:http://poj.org/problem?id=1821

题目给出长度为N的线段,需要m个人去覆盖,每小段可以覆盖也可以不覆盖,没人都可选择覆盖和不覆盖,如果覆盖的话一定要覆盖Si,而且长度不能超过Li,每一段的报酬是Pi,问最高多少报酬。

显然状态就是前i个人覆盖前j段。转移的初始状态要考虑第i个人没有任何行为或者不刷第j块的最优值。

由于i,j固定之后,决策空间的初始大小是固定的,每次j增大的时候,右端点不变,左端点线性变化,所以用一个单调队列维护一个函数calc()的严格递减序列的索引即可。

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 16010,M = 110;
int f[M][N];
struct node{
    int l,p,s;
    bool operator < (const node& b)const {
        return s<b.s;
    }
}a[N];
int calc(int i,int k){
    return f[i-1][k]-a[i].p*k;
}
int n,m;
int q[N];
int main(){
    cin>>n>>m;
    for(int i=1;i<=m;i++)
        scanf("%d %d %d",&a[i].l,&a[i].p,&a[i].s);
    sort(a+1,a+m+1);
    
    for(int i=1;i<=m;i++)
    {    
        int l=1,r=0;//队列范围[l,r] ,初始化队列 
        for(int k=max(0,a[i].s-a[i].l);k<=a[i].s-1;k++){
            while(l<=r && calc(i,q[r])<=calc(i,k))r--;
            q[++r]=k; 
        }
        for(int j=1;j<=n;j++){
            f[i][j]=max(f[i-1][j],f[i][j-1]);//什么也不做或者是空着不刷
            if(j>=a[i].s && j<=a[i].s+a[i].l-1){
                while(l<=r && q[l] < j-a[i].l) l++;
                if(l<=r)//队列不为空才能计算 
                f[i][j]=max(f[i][j],a[i].p*j+calc(i,q[l]));
            }
            
        }
    }
    
    int ans=0;
    for(int i=1;i<=n;i++)
        ans=max(ans,f[m][i]);
    cout<<ans<<endl;
        
}

 

 

posted @ 2020-08-03 08:32  WA自动机~  阅读(111)  评论(0编辑  收藏  举报