【Ybtoj 第23章例2】粉刷木板【单调队列】

在这里插入图片描述


解题思路

f [ i , j ] f[i,j] f[i,j]表示前i个木匠粉刷了前j个木板(可以有木板不粉刷)可知:

如果新加入的一个木匠要粉刷一段木块,设他粉刷的区间为 [ k + 1 , j ] [k+1,j] [k+1,j]
f [ i ] [ j ] = m a x j − l i < = k < s i ( f [ i − 1 , k ] + p i ∗ ( j − k ) ) ; ( j > = s i ) f[i][j]=max_{j-l_i<=k<s_i}(f[i-1,k]+p_i*(j-k)) ;(j>=s_i) f[i][j]=maxjli<=k<si(f[i1,k]+pi(jk))(j>=si)

我们把j提出来,是后面的式子只和k有关:
f [ i ] [ j ] = p i ∗ j + m a x j − l i < = k < s i ( f [ i − 1 , k ] − p i ∗ k ) ; ( j > = s i ) f[i][j]=p_i*j+max_{j-l_i<=k<s_i}(f[i-1,k]-p_i*k); (j>=s_i) f[i][j]=pij+maxjli<=k<si(f[i1,k]pik)(j>=si)

最后用单调队列维护 f [ i − 1 , k ] − p i ∗ k f[i-1,k]-p_i*k f[i1,k]pik的最大值即可。

//代码中的 k 表 示 i , i 分 别 表 示 k 和 j k表示i,i分别表示k和j kiikj


代码

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define ll long long
using namespace std;

int n,m;
ll dp[20010],x[20010],q[20010],w[20010];

struct c{
	int l,p,s;
}a[20010];

bool cmp(c l,c r){
	return l.s<r.s;
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
		scanf("%d%d%d",&a[i].l,&a[i].p,&a[i].s);
	sort(a+1,a+m+1,cmp);
	for(int k=1;k<=m;k++)
	{
		int s=a[k].s,p=a[k].p,l=a[k].l;
		int mn=max(0,s-l),mx=min(n,s+l);
		int h=1,t=0;
		for(int i=mn;i<=mx;i++)
			x[i]=dp[i]-p*i;
		for(int i=mn;i<s;i++)//从上一个木匠的k转移过来时,枚举k成立的范围
		{
			while(h<=t&&q[t]<=x[i])t--;
			q[++t]=x[i];
			w[t]=i;
		}
		for(int i=s;i<=mx;i++)//当前木匠可粉刷到的范围,枚举j成立的范围
		{
			while(h<=t&&w[h]<i-l)h++;
			if(h>t)break;
			dp[i]=max(dp[i],q[h]+p*i);
		}
		for(int i=2;i<=n;i++)
			dp[i]=max(dp[i],dp[i-1]);
	}
	printf("%lld",dp[n]);
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值