单调队列优化dp
我们将每个人的s值排序,这样我们就能保证当前这个人刷的木板一定在上一个人之后,我们就能进行线型dp
定义f[i][j]表示前i个人刷前j个木板获得的最多报仇,那么有
- 第i个人不工作,此时f[i][j]=f[i-1][j]
- 第j个木板空着,此时f[i][j]=f[i][j-1]
- 第i个人刷[k+1,j]木板,由题意得k+1≤Si≤j并且j-k≤Li,于是存在状态转移方程
在dp过程中,我们假定外层变量i为定值,当j增大时,不难发现k的取值范围上界不变,下界变大。我们不妨比较一下两个决策k1,k2,假设k1<k2≤Si-1
因为k2的位置靠后,所以k1最更早的被排除,如果此时满足
说明k2不仅更优,而且它存活的时间更长,我们就可以将k1排除。
因此,我们可以建立一个单调队列,存储决策允许集合并且保证决策点k单调递增,数值f[i-1][k]-Pi*k单调递减的序列。每次我们取出队头决策(最优)进行转移即可。
所以,我们就可以设计dp解决问题了
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 typedef long long ll; 7 inline int read() { 8 int ret=0; 9 int op=1; 10 char c=getchar(); 11 while(c<'0'||c>'9') {if(c=='-') op=-1; c=getchar();} 12 while(c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar(); 13 return ret*op; 14 } 15 struct node { 16 int l,s,p; 17 bool operator <(const node &x) const { 18 return s<x.s; 19 } 20 }a[110]; 21 int n,m,f[110][16010]; 22 int q[16010],l,r; 23 inline int calc(int i,int k) { 24 return f[i-1][k]-a[i].p*k; 25 } 26 int main() { 27 n=read(); m=read(); 28 for(int i=1;i<=m;i++) { 29 a[i].l=read(); 30 a[i].p=read(); 31 a[i].s=read(); 32 } 33 sort(a+1,a+m+1); 34 for(int i=1;i<=m;i++) { 35 l=1,r=0; 36 for(int k=max(0,a[i].s-a[i].l);k<=a[i].s-1;k++) { 37 while(l<=r&&calc(i,k)>=calc(i,q[r])) r--; 38 q[++r]=k; 39 } 40 for(int j=1;j<=n;j++) { 41 f[i][j]=max(f[i-1][j],f[i][j-1]); 42 if(j>=a[i].s) { 43 while(l<=r&&j-a[i].l>q[l]) l++; 44 if(l<=r) f[i][j]=max(f[i][j],calc(i,q[l])+j*a[i].p); 45 } 46 } 47 } 48 printf("%d\n",f[m][n]); 49 return 0; 50 }AC Code