一排N个木板,M个工匠站在不同位置$S_i$,每个人可以粉刷覆盖他位置的、最长长度为$L_i$木板段,每刷一个有$P_i$报酬。同一木板只刷一次。求最大报酬。
根据每个人的位置dp,设$f[i][j]$为第$i$个人刷前$j$个木板的最大报酬。$j<s[i]$表示不刷。
那就有
$f[i][j]=max{f[i-1][k]+(j-k)*p[i]} $ $ (s[i]≤j<s[i]+L[i] , s[i]-L[i]≤k<j)$
然后对于每行,相当于j从前面$l[i]$块选。拆开来。
$f[i][j]=max{f[i-1][k]-k*p[i]}+j*p[i] $ $ (s[i]≤j<s[i]+L[i] , s[i]-L[i]≤k<j)$
然后里面跟决策点k有关,在每行$s[i]$前面$l[i]$个位置把决策塞进一个$f[i-1][k]-k*p[i]$单调减,$k$单调增的单调队列里,然后每行取队头,及时排除、转移即可。
代码写繁了。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 typedef long long ll; 8 template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;} 9 template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;} 10 template<typename T>inline T _min(T A,T B){return A<B?A:B;} 11 template<typename T>inline T _max(T A,T B){return A>B?A:B;} 12 template<typename T>inline T read(T&x){ 13 x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1; 14 while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x; 15 } 16 const int N=35000+7; 17 int f[2][N],q[N]; 18 struct hinanawitenshi{ 19 int p,l,s; 20 }a[107]; 21 int n,m,tmp,l,r; 22 inline char cmp(hinanawitenshi A,hinanawitenshi B){return A.s<B.s;} 23 inline int F(int k,int i,int now){return f[now^1][k]-k*a[i].p;} 24 25 int main(){//freopen("tmp.in","r",stdin);freopen("tmp.out","w",stdout); 26 read(n),read(m); 27 for(register int i=1;i<=m;++i)read(a[i].l),read(a[i].p),read(a[i].s); 28 sort(a+1,a+m+1,cmp); 29 for(register int i=1,now=1;i<=m;++i,now^=1){ 30 tmp=0;l=1,r=0; 31 for(register int j=1;j<a[i].s;++j)f[now][j]=f[now^1][j]; 32 for(register int k=_max(a[i].s-a[i].l,0);k<a[i].s;++k){ 33 while(l<=r&&F(k,i,now)>=F(q[r],i,now))--r; 34 q[++r]=k; 35 } 36 for(register int j=a[i].s;j<=_min(a[i].s+a[i].l-1,n);++j){ 37 while(l<=r&&q[l]<j-a[i].l)++l; 38 f[now][j]=_max(f[now^1][j],F(q[l],i,now^1)+j*a[i].p); 39 MAX(tmp,f[now][j]); 40 } 41 for(register int j=a[i].s+a[i].l;j<=n;++j)f[now][j]=_max(f[now^1][j],tmp);//MISTAKE:之后的状态忘转移了。 42 } 43 printf("%d ",f[m&1][n]); 44 return 0; 45 }