P4480-[BJWC2018]餐巾计划问题【三分,贪心】

本文解析洛谷P4480餐巾问题,介绍一种基于贪心策略结合三分搜索的高效算法。针对不同清洗方案,通过决策后延策略实现最优费用计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

正题

题目链接:https://www.luogu.com.cn/problem/P4480


题目大意

n n n天,第 i i i天需要 a i a_i ai个餐巾。

每个餐巾价格为 p p p,使用完后有两种清洗方法

  1. 清洗 m 1 m_1 m1天,费用为 c 1 c_1 c1
  2. 清洗 m 2 m_2 m2天,费用为 c 2 c_2 c2

求满足所有需求的最小花费

1 ≤ n ≤ 2 × 1 0 5 , 1 ≤ m 1 , m 2 ≤ n , 1 ≤ c 1 , c 2 , a i ≤ 100 1\leq n\leq 2\times 10^5,1\leq m_1,m_2\leq n,1\leq c_1,c_2,a_i\leq 100 1n2×105,1m1,m2n,1c1,c2,ai100


解题思路

如果固定了购买的毛巾数量那么此题就有一个贪心的做法。

首先先把购买的全部优先使用了

然后如果快的那个洗法费用更低,那么显然全部用快的洗法。

否则就是一个快但是贵,一个慢但是便宜。那么我们的决策就是能用慢的就不使用快的。这个可以用决策后延来解决,当我们需要毛巾的时候再决定前面的毛巾的洗法。

这样的贪心是 O ( n ) O(n) O(n)的,但是我们不能暴力枚举毛巾数量。

感性理解的话不难费用根据毛巾的数量呈一个单谷状,所以我们可以直接三分出这个谷底即可。

时间复杂度 O ( n log ⁡ ( 100 n ) ) O(n\log (100n)) O(nlog(100n)),因为 d e q u e deque deque比较慢所以我的标要开 -O2 \text{-O2} -O2才能过/kk


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=2e5+10,inf=2e9;
int n,p,m1,m2,c1,c2,w[N];
struct node{
	int t,w;
	node(int tt=0,int ww=0)
	{t=tt;w=ww;return;}
};
deque<node> q1,q2,q3;
int calc(int x){
	int ans=x*p;
	q1.clear();q2.clear();q3.clear();
	for(int i=1;i<=n;i++){
		while(!q1.empty()&&q1.front().t+m1<=i)
			q2.push_back(q1.front()),q1.pop_front();
		while(!q2.empty()&&q2.front().t+m2<=i)
			q3.push_back(q2.front()),q2.pop_front();
		int v=w[i],tmp=min(v,x);x-=tmp;v-=tmp;
		while(v&&!q3.empty()){
			tmp=min(v,q3.back().w);
			v-=tmp;ans+=tmp*c2;
			if(tmp==q3.back().w)q3.pop_back();
			else q3.back().w-=tmp;
		}
		while(v&&!q2.empty()){
			tmp=min(v,q2.back().w);
			v-=tmp;ans+=tmp*c1;
			if(tmp==q2.back().w)q2.pop_back();
			else q2.back().w-=tmp;
		}
		if(v)return inf;
		q1.push_back(node(i,w[i]));
	}
	return ans;
}
int main()
{
	scanf("%d%d%d%d%d%d",&n,&m1,&m2,&c1,&c2,&p);
	if(m1>m2)swap(m1,m2),swap(c1,c2);
	if(c1<c2)c2=c1,m2=m1;
	for(int i=1;i<=n;i++)scanf("%d",&w[i]);
	int l=1,r=n*100;
	while(l<=r){
		int mid=(l+r)>>1;
		if(calc(mid)<calc(mid+1))r=mid-1;
		else l=mid+1;
	}
	printf("%d\n",calc(l));
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值