【单调队列优化】CF319C——Kalila and Dimna in the Logging Industry

前言

本来挺简单的一道题...

我发现自己不太擅长简化、抽象模型,老是不知道怎么把学过的技巧用上去_(:зゝ∠)_

题目

Kalila and Dimna are two jackals living in a huge jungle. One day they decided to join a logging factory in order to make money.

The manager of logging factory wants them to go to the jungle and cut n trees with heights a1, a2, ..., an. They bought a chain saw from a shop. Each time they use the chain saw on the tree number i, they can decrease the height of this tree by one unit. Each time that Kalila and Dimna use the chain saw, they need to recharge it. Cost of charging depends on the id of the trees which have been cut completely (a tree is cut completely if its height equal to 0). If the maximum id of a tree which has been cut completely is i (the tree that have height ai in the beginning), then the cost of charging the chain saw would be bi. If no tree is cut completely, Kalila and Dimna cannot charge the chain saw. The chainsaw is charged in the beginning. We know that for each i < j, ai < aj and bi > bj and also bn = 0 and a1 = 1. Kalila and Dimna want to cut all the trees completely, with minimum cost.

They want you to help them! Will you?

Input

The first line of input contains an integer n (1 ≤ n ≤ 105). The second line of input contains n integers a1, a2, ..., an (1 ≤ ai ≤ 109). The third line of input contains n integers b1, b2, ..., bn (0 ≤ bi ≤ 109).

It's guaranteed that a1 = 1, bn = 0, a1 < a2 < ... < an and b1 > b2 > ... > bn.

Output

The only line of output must contain the minimum cost of cutting all the trees completely.

Please, do not write the %lld specifier to read or write 64-bit integers in С++. It is preferred to use the cin, cout streams or the %I64d specifier.

Examples

Input

5
1 2 3 4 5
5 4 3 2 0

Output

25

Input

6
1 2 3 10 20 30
6 5 4 3 2 0

Output

138

题目大意

伐木工人用电锯伐木,一共需要砍n棵树,每棵树的高度为a[i],每次砍伐只能砍1单位高度,之后需要对电锯进行充电,费用为当前砍掉的树中最大id的b[id]值

a[1] = 1 , b[n] = 0,a[i]<a[i+1],b[i]>b[i+1]

问砍完所有的树的最小费用

分析

参考博客&特别鸣谢:

https://www.cnblogs.com/yejinru/p/3348326.html

https://blog.csdn.net/lerenceray/article/details/9365947

首先,由于b[n] = 0 , 所以很容易弄出一个O(n^2)的状态转移方程:

dp[1]=0;
for(int i=2;i<=n;i++)
{
    dp[i]=INF;
    for(int j=1;j<i;j++)
        dp[i]=min(dp[i],dp[j]+b[j]*a[i]);
}

这种朴素的转移方程显然会TLE

注意到以上的方程,其实就是1D1D模型(具体百度),可以利用斜率进行优化

题意大致是砍树,不一定按顺序砍,目的是为了使充电费用最低,看完标号最大的树,充电就不再需要费用,所以关键在于求砍完标号最大的树所需要的最小花费

DP优化,记dp[ i ]为砍倒第i棵树所需要的最小花费,

所以所以对于dp[ n ], dp[ n ] = min{ dp[ j ] + b[ j ] * a[ n ] }, j = 1, 2, 3, ...... n - 1

注意这里题目条件单调的,这可以作为斜率DP的一个标志


分析DP的具体操作:

设现在要求dp[ k ],在k之前的任意两个数,我们设为i, j, 且i < j < k

那么dp[ j ] + b[ j ] * a[ k ] 和 dp[ i ] + b[ i ] * a[ k ]两个方案那个更优呢?

作差比较, 移项化简得:如果 (dp[ j ] - dp[ i ]) / (b[ i ] - b[ j ]) < a[ k ] 则 方案 j 优于方案 i(这里要注意b[  ]递减,移项变号),

我们把左边看作一个两点的斜率,得到一个函数G(j, i)如果G(j, i)< a[ k ] 则 j 优于 i,可以删除 i 方案,且以后 j 之后再有其他数,i 也不可能成为最优方案,因为 j 比 i 优

另外 当G(j, i) < G ( k , j )时, 可以删除 j 方案,因为这样 j 永远不会是最优方案, 以上两步优化维护了一个单调队列, 图形上看相邻两点的斜率单调递增或递减,所以叫斜率DP优化吧?

原来复杂度n*n的降为了O(n),因为每个方案之进入队列一次,被提出以后不再加入

简单来说:

斜率优化无非是:假设j<k,有以下关系:

dp[ k ]+b[ k ] * a[ i ] < dp[ j ]+b[ j ]*a[ i ]

由于b[ k ]<b[ j ]

因此移项之后为:

( dp[ k ] - dp[ j ] ) / ( b[ j ] - b[ k ] ) < a[ i ]

因此,我们可以根据斜率进行优化,具体可以看代码,这部分比较好懂

Ps.可是为什么head=1,tail=2才可以呢...又是区间开闭的问题吗...?

代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=1e5;
ll a[MAXN+5],b[MAXN+5],que[MAXN+5],dp[MAXN+5];
ll n;
ll f1(ll x,ll y)
{
	return dp[que[x]]-dp[que[y]];
}
ll f2(ll x,ll y)
{
	return b[que[x]]-b[que[y]];
}
void Solve()
{
	ll head=1,tail=2;
	que[1]=1,dp[1]=0;
	for(int i=2;i<=n;i++)
	{
		while(head<tail-1&&f1(head+1,head)<f2(head,head+1)*a[i])
			head++;
		dp[i]=dp[que[head]]+b[que[head]]*a[i];
		while(head<tail-1&&tail>2&&(double)f1(tail-1,tail-2)/f2(tail-2,tail-1)>=(double)(dp[i]-dp[que[tail-1]])/(b[que[tail-1]]-b[i]))
			tail--;
		que[tail++]=i;
	}
	printf("%lld\n",dp[n]);
}
int main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	for(int i=1;i<=n;i++)
		scanf("%lld",&b[i]);
	Solve();
	return 0;
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
疫情居家办公系统管理系统按照操作主体分为管理员和用户。管理员的功能包括办公设备管理、部门信息管理、字典管理、公告信息管理、请假信息管理、签到信息管理、留言管理、外出报备管理、薪资管理、用户管理、公司资料管理、管理员管理。用户的功能等。该系统采用了MySQL数据库,Java语言,Spring Boot框架等技术进行编程实现。 疫情居家办公系统管理系统可以提高疫情居家办公系统信息管理问题的解决效率,优化疫情居家办公系统信息处理流程,保证疫情居家办公系统信息数据的安全,它是一个非常可靠,非常安全的应用程序。 管理员权限操作的功能包括管理公告,管理疫情居家办公系统信息,包括外出报备管理,培训管理,签到管理,薪资管理等,可以管理公告。 外出报备管理界面,管理员在外出报备管理界面中可以对界面中显示,可以对外出报备信息的外出报备状态进行查看,可以添加新的外出报备信息等。签到管理界面,管理员在签到管理界面中查看签到种类信息,签到描述信息,新增签到信息等。公告管理界面,管理员在公告管理界面中新增公告,可以删除公告。公告类型管理界面,管理员在公告类型管理界面查看公告的工作状态,可以对公告的数据进行导出,可以添加新公告的信息,可以编辑公告信息,删除公告信息
编写一个 SQL 查询,筛选出过去一年中订单总量 少于10本 的 书籍 ,不考虑 上架(available from)距今 不满一个月 的书籍。并且 假设今天是 2019-06-23。 建表语句如下: Create table If Not Exists Books (book_id int, name varchar(50), available_from date); Create table If Not Exists Orders (order_id int, book_id int, quantity int, dispatch_date date); Truncate table Books; insert into Books (book_id, name, available_from) values ('1', 'Kalila And Demna', '2010-01-01'); insert into Books (book_id, name, available_from) values ('2', '28 Letters', '2012-05-12'); insert into Books (book_id, name, available_from) values ('3', 'The Hobbit', '2019-06-10'); insert into Books (book_id, name, available_from) values ('4', '13 Reasons Why', '2019-06-01'); insert into Books (book_id, name, available_from) values ('5', 'The Hunger Games', '2008-09-21'); Truncate table Orders; insert into Orders (order_id, book_id, quantity, dispatch_date) values ('1', '1', '2', '2018-07-26'); insert into Orders (order_id, book_id, quantity, dispatch_date) values ('2', '1', '1', '2018-11-05'); insert into Orders (order_id, book_id, quantity, dispatch_date) values ('3', '3', '8', '2019-06-11'); insert into Orders (order_id, book_id, quantity, dispatch_date) values ('4', '4', '6', '2019-06-05'); insert into Orders (order_id, book_id, quantity, dispatch_date) values ('5', '4', '5', '2019-06-20'); insert into Orders (order_id, book_id, quantity, dispatch_date) values ('6', '5', '9', '2009-02-02'); insert into Orders (order_id, book_id, quantity, dispatch_date) values ('7', '5', '8', '2010-04-13');
07-15
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值