题目链接: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; }