zoukankan      html  css  js  c++  java
  • 【洛谷P4480】餐巾计划问题

    题目

    题目链接:https://www.luogu.com.cn/problem/P4480
    双倍经验:https://www.luogu.com.cn/problem/P2223
    三倍经验:https://www.luogu.com.cn/problem/P1251
    四倍经验:https://www.luogu.com.cn/problem/P2917
    一个餐厅在相继的 (n) 天里,每天需用的餐巾数不尽相同。假设第 (i)((i=1, 2, ..., n)) 需要 (r_i) 块餐巾。餐厅可以在任意时刻购买新的餐巾,每块餐巾的费用为 (p) 。使用过的旧餐巾,则需要经过清洗才能重新使用。把一块旧餐巾送到清洗店A,需要等待 (m_1) 天后才能拿到新餐巾,其费用为 (c_1) ;把一块旧餐巾送到清洗店B,需要等待 (m_2) 天后才能拿到新餐巾,其费用为 (c_2) 。例如,将一块第 (k) 天使用过的餐巾送到清洗店A清洗,则可以在第 (k+m_1) 天使用。
    请为餐厅合理地安排好 (n) 天中餐巾使用计划,使总的花费最小。
    (nleq 2 imes 10^5)

    思路

    弱化版是费用流解法。加强后考虑贪心。
    首先如果设买 (i) 条毛巾所需要的总花费为 (f(i)),那么 (f(i)) 是一个单峰函数。当买的毛巾小于最优解时,洗毛巾的费用就会增加;多于最优解时,买毛巾的费用就会增加。
    (m_1leq m_2),如果此时 (c_1leq c_2),那么显然第二种清洗店不会有任何作用,直接让 (m_2=m_1,c_2=c_1) 即可。
    所以可以三分买毛巾的数量,然后考虑如何计算买 (k) 条毛巾时的最小花费。
    对于其中一天,我们肯定是依次考虑这三种操作:

    • 如果有买了没用的毛巾,那么就立刻使用。显然往后留不会更优。
    • 如果有毛巾在 (m_2) 天前使用了,那么就使用这个毛巾。也就是能慢洗就慢洗。
    • 如果有毛巾在 ([m_1,m_2)) 天前使用了,那么就使用这个毛巾。注意此时需要选择时间尽量后的毛巾使用,因为时间较前的毛巾可能再等待若干天就可以送去慢洗了。

    那么维护三个队列 (q1,q2,q3),分别维护上一次使用时间在 ([1,m_1),[m_1,m_2),[m_2,+infty)) 内的毛巾。每当来到新的一天,就不断尝试将 (q1) 的队头扔到 (q2) 队尾,(q2) 队头扔到 (q3) 队尾。如果某一天所有毛巾都用完了还不够,那么返回 (+infty) 就好。
    时间复杂度 (O(nlog (np)))

    代码

    #include <bits/stdc++.h>
    #define mp make_pair
    #define fi first
    #define se second
    using namespace std;
    
    const int N=200010,Inf=2147483647;
    int n,m1,m2,c1,c2,p,a[N];
    pair<int,int> q1[N],q2[N],q3[N];
    
    int solve(int mid)
    {
    	if (mid<0) return Inf;
    	int h1=1,h2=1,h3=1,t1=0,t2=0,t3=0,ans=mid*p;
    	for (int i=1;i<=n;i++)
    	{
    		for (;h1<=t1 && q1[h1].fi+m1<=i;h1++) q2[++t2]=q1[h1];
    		for (;h2<=t2 && q2[h2].fi+m2<=i;h2++) q3[++t3]=q2[h2];
    		int x=a[i],y=min(x,mid);
    		if (mid) mid-=y,x-=y,q1[++t1]=mp(i,y);
    		while (x && h3<=t3)
    			if (q3[h3].se>x)
    			{
    				q3[h3].se-=x; ans+=x*c2;
    				if (q1[t1].fi!=i) q1[++t1]=mp(i,x);
    					else q1[t1].se+=x;
    				x=0;
    			}
    			else
    			{
    				x-=q3[h3].se; ans+=q3[h3].se*c2;
    				if (q1[t1].fi!=i) q1[++t1]=mp(i,q3[h3].se);
    					else q1[t1].se+=q3[h3].se;
    				h3++;
    			}
    		while (x && h2<=t2)
    			if (q2[t2].se>x)
    			{
    				q2[t2].se-=x; ans+=x*c1;
    				if (q1[t1].fi!=i) q1[++t1]=mp(i,x);
    					else q1[t1].se+=x;
    				x=0;
    			}
    			else
    			{
    				x-=q2[t2].se; ans+=q2[t2].se*c1;
    				if (q1[t1].fi!=i) q1[++t1]=mp(i,q2[t2].se);
    					else q1[t1].se+=q2[t2].se;
    				t2--;
    			}
    		if (x) return Inf;
    	}
    	return ans;
    }
    
    int main()
    {
    	scanf("%d%d%d%d%d%d",&n,&m1,&m2,&p,&c1,&c2);
    	m1++; m2++;
    	for (int i=1;i<=n;i++)
    		scanf("%d",&a[i]);
    	if (m1>=m2) swap(m1,m2),swap(c1,c2);
    	if (c1<=c2) m2=m1,c2=c1;
    	int l=1,r=20000000,mid;
    	while (r-l>=3)
    	{
    		mid=(l+r)>>1;
    		if (solve(mid)<solve(mid+1)) r=mid;
    			else l=mid;
    	}
    	int ans=Inf;
    	for (int i=l;i<=r;i++) ans=min(ans,solve(i));
    	cout<<ans;
    	return 0;
    }
    
  • 相关阅读:
    webrtc系列之-像老鼠一样打洞
    Ubuntu记录用户IP访问操作信息工具
    OPENVIDU实现同一用户同时发布多个流媒体
    如何使用Nginx-rtmp搭建简易的HLS直播系统
    python实现数据库主从状态监控
    简单分析实现运维利器---批量操作bashshell
    《我与Windows Server 2008R2那点事儿》之域控账户故障事件
    常用动态路由协议之IS-IS
    在云服务器上搭建Python开发环境
    基于Python3接口自动化测试开发相关常用方法
  • 原文地址:https://www.cnblogs.com/stoorz/p/15037062.html
Copyright © 2011-2022 走看看