$Poj1821 Fence $单调队列优化$DP$

Poj   Acwing

Description

有N块木板等待被M个工匠粉刷,每块木板至多被刷一次.第i个工匠要么不粉刷,要么粉刷包含木块Si的,长度不超过Li的连续的一段木板,每粉刷一块可以得到Pi的报酬.求如何安排能使工匠们获得的总报酬最多.

1<=N<=16000,1<=M<=100

Sol

先把所有工匠按照Si从小到大排序,使我们能够按顺序进行线性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块木板 $F[i][j]=max(F[i-1][k]+Pi*(j-k)) (j-Li<=k<=Si-1,j>=Si)$

然后用单调队列优化第3个式子

$F[i][j]=Pi*j+max(F[i-1][k]-Pi*k)$ 单调队列维护$max(F[i-1][k]-Pi*k)$就OK了

单调队列维护时元素的进出序列要弄清楚是队首还是队尾,不要搞混了 (大概也只有我会搞混 $ovo$

Code

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<deque>
 4 #include<algorithm>
 5 #define il inline
 6 #define Rg register
 7 #define fr front
 8 #define bk back
 9 #define go(i,a,b) for(Rg int i=a;i<=b;i++)
10 #define yes(i,a,b) for(Rg int i=a;i>=b;i++)
11 using namespace std;
12 il int read()
13 {
14     int x=0,y=1;char c=getchar();
15     while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
16     while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
17     return x*y;
18 }
19 int n,m,f[101][16001];
20 deque<int>q;
21 struct node{int l,p,s;}a[101];
22 il int cal(int i,int x){return f[i-1][x]-a[i].p*x;}
23 il bool cmp(node x,node y){return x.s<y.s;}
24 int main()
25 {
26     n=read(),m=read();
27     go(i,1,m)a[i]=(node){read(),read(),read()};
28     sort(a+1,a+m+1,cmp);
29     go(i,1,m)
30     {
31         q.clear();
32         go(k,max(a[i].s-a[i].l,0),a[i].s-1)
33         {
34             while(q.size() && cal(i,q.bk())<=cal(i,k))q.pop_back();
35             q.push_back(k);
36         }
37         go(j,1,n)
38         {
39             f[i][j]=max(f[i][j-1],f[i-1][j]);
40             if(j>=a[i].s)
41             {
42                 while(q.size() && q.fr()<j-a[i].l)q.pop_front();
43                     if(q.size())f[i][j]=max(f[i][j],a[i].p*j+cal(i,q.fr()));
44             }
45         }
46     }
47       printf("%d
",f[m][n]);
48     return 0;
49 }
View Code

 

光伴随的阴影
原文地址:https://www.cnblogs.com/forward777/p/11248271.html