【POJ1821】Fence

单调队列优化dp

我们将每个人的s值排序,这样我们就能保证当前这个人刷的木板一定在上一个人之后,我们就能进行线型dp

定义f[i][j]表示前i个人刷前j个木板获得的最多报仇,那么有

  1. 第i个人不工作,此时f[i][j]=f[i-1][j]
  2. 第j个木板空着,此时f[i][j]=f[i][j-1]
  3. 第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
",f[m][n]);
49     return 0;
50 }
AC Code
原文地址:https://www.cnblogs.com/shl-blog/p/10989144.html