粉刷木板(dp)(单调队列)
Posted SSL_LKJ
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了粉刷木板(dp)(单调队列)相关的知识,希望对你有一定的参考价值。
粉刷木板
输入样例
8 4
3 2 2
3 2 3
3 3 5
1 1 7
输出样例
17
解题思路
这题乍一看是道dp
设
f
i
,
j
f_i,j
fi,j为前i个粉刷匠,刷了前j个木块
(注:可以有木块不选)
那就会有三种情况
1.当前粉刷匠不刷任何一个木块
2.当前粉刷匠不刷当前木块
3.当前粉刷匠刷一段区间的木块
前面两个的转移方程很简单,就不用多讲了
主要看第三个,设粉刷区间 [ k + 1 , j ] [k+1,j] [k+1,j]
m a x j − l i ⩽ k < s i f i − 1 , k + p i ∗ ( j − k ) ( s i ⩽ j ) max_j-l_i\\leqslant k<s_i ~~~f_i-1,k+p_i*(j-k)(s_i\\leqslant j) maxj−li⩽k<si fi−1,k+pi∗(j−k)(si⩽j)
但是很明显,正常dp会超时
所以我们可以想办法化简一下这个式子,试试提取出 j j j
p i ∗ j + m a x j − l i ⩽ k < s i f i − 1 , k − p i ∗ k ( s i ⩽ j ) p_i*j+~max_j-l_i\\leqslant k<s_i ~~~f_i-1,k-p_i*k(s_i\\leqslant j) pi∗j+ maxj−li⩽k<si fi−1,k−pi∗k(si⩽j)
发现 m a x j − l i ⩽ k < s i f i − 1 , k − p i ∗ k ( s i ⩽ j ) max_j-l_i\\leqslant k<s_i ~~~f_i-1,k-p_i*k(s_i\\leqslant j) maxj−li⩽k<si fi−1,k−pi∗k(si⩽j)是可以被单调队列维护的
所以就AC了
AC代码
#include<algorithm>
#include<cstdio>
using namespace std;
int m,n,q[4000005],f[205][20005];
struct node
int l,p,s;
a[20005];
bool cmp(node x,node y)
return x.s<y.s;
int main()
scanf("%d%d",&m,&n);
for(int i=1;i<=n;i++)
scanf("%d%d%d",&a[i].l,&a[i].p,&a[i].s);
sort(a+1,a+n+1,cmp);//记住排序
for(int i=1;i<=n;i++)//dp
int head=1,tail=0;
for(int j=max(a[i].s-a[i].l,0);j<=a[i].s-1;j++)//单调队列维护最大值
while(head<=tail&&f[i-1][q[tail]]-q[tail]*a[i].p<=f[i-1][j]-j*a[i].p)tail--;
q[++tail]=j;
for(int j=1;j<=m;j++)//dp
f[i][j]=max(f[i-1][j],f[i][j-1]);
if(j>=a[i].s)
while(head<=tail&&j-q[head]>a[i].l)head++;
if(head<=tail)f[i][j]=max(f[i][j],(j-q[head])*a[i].p+f[i-1][q[head]]);
printf("%d",f[n][m]);
return 0;
谢谢
以上是关于粉刷木板(dp)(单调队列)的主要内容,如果未能解决你的问题,请参考以下文章