码迷,mamicode.com
首页 > 其他好文 > 详细

POJ1821 Fence 单调队列优化DP

时间:2019-08-25 20:10:07      阅读:120      评论:0      收藏:0      [点我收藏+]

标签:--   过程   can   就是   problem   clu   amp   std   上下   

 

 

http://poj.org/problem?id=1821

 

题意:给长度为n的木板,k个工人,每个工人要么不粉刷,或者选择一个包含木板si,长度不超过li的连续的一段木板粉刷,每粉刷一块得到pi的报酬,问如何安排工人使得总报酬最大?

思路:可以按si给工人排序,这样我们就可以按照顺序依次安排工人。设f[i][j]表示到第i个工人,刷到前j块木板的最大报酬,

三种情况

  1. 工人不刷:f[i][j]=f[i-1][j]
  2. 木板空着:f[i][j]=f[i][j-1]
  3. 第i个工人刷k+1到j的木板,其中k,j满足k+1<=si<=j,报酬就是f[i][j]=f[i-1][k]+pi*(j-k),拆开得pi*j+(-pi*k+f[i-1][k])。可以发现对于每个j来说,要找到它对应的最大报酬,需要尝试范围内的所有k值,而实际上我们可以发现随着枚举j,j的增大,所对应的k的区间范围的上下界也是递增的,这样的话,我们就可以考虑使用单调队列来优化枚举k的过程,又因为要使报酬最大,所以我们需要维护一个单调递减的序列来维护-pi*k+f[i-1][k]这个只与k相关的最大值。
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=16005;
struct note
{
    int l,p,s;
} a[maxn];
int q[maxn];
bool operator <(note a,note b)
{
    return a.s<b.s;
}
int f[105][maxn];
int calc(int i,int k)
{
    return f[i-1][k]-a[i].p*k;
}
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1; i<=k; i++)
        scanf("%d%d%d",&a[i].l,&a[i].p,&a[i].s);
    sort(a+1,a+1+k);
    for(int i=1; i<=k; i++)
    {
        int l=1,r=0;
        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)
            {
                while(l<=r&&q[l]<j-a[i].l) l++;
                if(l<=r) f[i][j]=max(f[i][j],calc(i,q[l])+a[i].p*j);
            }
        }
    }
    printf("%d\n",f[k][n]);
}

 

POJ1821 Fence 单调队列优化DP

标签:--   过程   can   就是   problem   clu   amp   std   上下   

原文地址:https://www.cnblogs.com/dongdong25800/p/11408922.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!