Fence

POJ

题意:有\(N(N<=16000)\)块木板从左到右排成一行,有\(M(M<=100)\)工匠粉刷木板,每块木板最多被粉刷一次.第\(i\)个工匠要么不粉刷,要么粉刷包括木板\(S_i\)在内的长度不超过\(L_i\)的连续的一段木板,每粉刷一块木板可获得报酬\(p_i\),求最大总报酬.

分析:先按照\(S[i]\)从小到大排序,保证粉刷的木板是按从小到大的顺序来的,方便我们讨论.

\(f[i][j]\)表示安排前\(i\)个工匠粉刷前j块木板能够获得的最大报酬.

\(f[i][j]=f[i-1][j]\)\(i\)个工匠什么都不刷.

\(f[i][j]=f[i][j-1]\)\(j\)块木板可以不刷.

\(f[i][j]=max_{f[i-1][k]+P_i*(j-k)}\),当\(j-L_i<=k<=S_i-1\)时,即第\(i\)个工匠粉刷第\(k+1\)到第\(j\)块木板,因为\(k+1<=S_i<=j\)(\(S_i\)必须要被刷到),且\(j-k<=L_i\)(第\(i\)个工匠最多刷\(L_i\)块木板).

考虑对该转移式进行优化.\(f[i][j]=P_i*j+max_{f[i-1][k]-P_i*k}\),维护一个k单调递增,同时\(f[i-1][k]-P_i*k\)单调递减的队列.

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read(){
   int s=0,w=1;char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
   return s*w;
}
int q[16005],f[105][16005];
struct Work{
    int l,p,s;
}a[105];
inline bool cmp(const Work &x,const Work &y){return x.s<y.s;}
inline int C(int i,int k){return f[i-1][k]-a[i].p*k;}
int main(){
    int n=read(),m=read();
    for(int i=1;i<=m;i++)
        a[i].l=read(),a[i].p=read(),a[i].s=read();
    sort(a+1,a+m+1,cmp);
    for(int i=1;i<=m;i++){
        int l=1,r=0;
        for(int j=max(a[i].s-a[i].l,0);j<=a[i].s-1;j++){
            while(l<=r&&C(i,q[r])<=C(i,j))r--;
            q[++r]=j;
        }
        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],a[i].p*j+C(i,q[l]));
            }
        }
    }
    printf("%d\n",f[m][n]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/PPXppx/p/10940366.html