• 《算法竞赛进阶指南》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;
            
    }

     

  • 相关阅读:
    Linux 技巧
    设计模式大集合
    [Java] Design Pattern:Code Shape
    [Java] 设计模式: Code Shape
    BookNote: Refactoring
    读书笔记: 博弈论导论
    读书笔记: 博弈论导论
    读书笔记: 博弈论导论
    读书笔记: 博弈论导论
    读书笔记: 博弈论导论
  • 原文地址:https://www.cnblogs.com/randy-lo/p/13424343.html
Copyright © 2020-2023  润新知