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 }