粉刷木板(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) maxjlik<si   fi1,k+pi(jk)(sij)

但是很明显,正常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) pij+ maxjlik<si   fi1,kpik(sij)

发现 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) maxjlik<si   fi1,kpik(sij)可以被单调队列维护

所以就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)(单调队列)的主要内容,如果未能解决你的问题,请参考以下文章

粉刷木板(dp)(单调队列)

ybtoj 单调队列课堂过关 例题2POJ 1821DP粉刷木板 & Fence

POJ1821 Fence 单调队列优化DP

AcWing 298. 围栏 (POJ1821)

粉刷匠(字符串的dp问题)

[SCOI2009]粉刷匠(动态规划,序列dp,背包)