[多米诺骨牌单调栈模型]CodeForces 1131G. Most Dangerous Shark

该博客详细介绍了如何利用单调栈解决多米诺骨牌推倒问题,讨论了如何在O(n^2)的时间复杂度内计算每个点向左右推的最远距离,并提出一种O(n)的解决方案,通过维护单调栈来找到能推倒当前点的点中的最小值,从而得到最小总花费。

题目

题目简述:从左到右依次有 n ≤ 1 0 7 n≤10^7 n107个Domino骨牌,高度为 h i hi hi,手动推倒他的花费为 c i ci ci。每个骨牌之间的距离为 1 1 1。一个骨牌可以被向左或者向右推倒。当第 i i i个骨牌被推倒时,他会以相同方向推倒与其距离 &lt; h i &lt;hi <hi的所有骨牌。求推倒所有骨牌的最小花费。( P S : PS: PS:输入格式和题目描述相差甚远,及其恶心,不过我们能 i d e n t i f y   w i t h   h i s   e f f o r t   t o   r e d u c e   t h e   I O   t i m e \rm{identify \ with \ his \ effort \ to \ reduce \ the \ IO \ time} identify with his effort to reduce the IO time)

data structures      dp      two pointers      *2900

实际上我们 O ( n 2 ) O(n^2) O(n2)算出每个点向左(右)推的最远距离然后就可以 O ( n 2 ) O(n^2) O(n2)DP了。

  1. 每个点向右推的最远距离如何 O ( n ) O(n) O(n)?

有一个很显然的性质:
假设 i i i向右推,能倒的最远的位置为 j j j
如果满足 j ≥ i + 1 j\geq i+1 ji+1,那么将 i + 1 i+1 i+1向右推,能倒的最远位置一定不超过 j j j
因为 i i i能推倒 i + 1 i+1 i+1,所以 i + 1 i+1 i+1倒了能推倒的位置i就一定能推倒。

发现如果我们把一个点和它往右推的最远位置连一条线,会是这个样子:
(是不是很形象?)

所以我们从左往右枚举点,每次把当前点加入单调栈中,如果当前点不能被栈顶点直接覆盖,那么栈顶点刚好不能覆盖当前点,最远覆盖为当前点-1,被弹出。
栈中相当于存着一层层包含,没有并列的弧,每次加入新弧,尝试弹出之前的弧并更新答案。

  1. 如何 O ( n ) O(n) O(n)计算答案?
    f [ i ] = min ⁡ { f [ L [ i ] ] + c i , min ⁡ j &lt; i &lt; R [ j ] { f [ j − 1 ] + c j } } f[i] = \min\left\{ f[L[i]]+c_i, \min_{j &lt; i &lt; R[j]} \{f[j-1]+c_j\} \right\} f[i]=min{f[L[i]]+ci,j<i<R[j]min{f[j1]+cj}}
    f [ L [ i ] ] + c i f[L[i]]+c_i f[L[i]]+ci很简单,看后面的。
    现在我们要在上面那个图形中找出能推倒 i i i的点中的最小值。
    发现能推倒 i i i的点的图案组成了一层层包含,没有并列的弧。
    然后就直接单调栈维护,还同时维护一下栈内最小值(其实就是保证栈顶最小即可)就完了。

单调栈和单调队列,重要的是两单调
1:加入时间单调:队列的是队头最早,栈是栈顶最晚。
2:答案单调:取头一定最优,只是有可能不合法。

AC Code:

#include<bits/stdc++.h>
#define maxn 10000007
#define LL long long
using namespace std;

int n,m,siz[maxn],cnt,h[maxn],fl[maxn],fr[maxn];
LL c[maxn],f[maxn];
vector<int>H[maxn],C[maxn];
int q[maxn],R;

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",&siz[i]);H[i].resize(siz[i]),C[i].resize(siz[i]);
		for(int j=0;j<siz[i];j++)
			scanf("%d",&H[i][j]);
		for(int j=0;j<siz[i];j++)
			scanf("%d",&C[i][j]);
	}
	int Q;scanf("%d",&Q);
	for(int i=1;i<=Q;i++){
		int id,mul;scanf("%d%d",&id,&mul);
		for(int j=0;j<siz[id];j++)
			h[++cnt] = H[id][j] , c[cnt] = C[id][j] * 1ll * mul;
	}
	for(int i=1;i<=m;i++){
		for(;R && i>=q[R-1]+h[q[R-1]];R--)
			fr[q[R-1]] = i-1;
		q[R++] = i;
	}
	for(;R;R--) fr[q[R-1]]=m;
	for(int i=m;i>=1;i--){
		for(;R && i<=q[R-1]-h[q[R-1]];R--)
			fl[q[R-1]]=i+1;
		q[R++]=i;
	}
	for(;R;R--) fl[q[R-1]]=1;
	for(int i=1;i<=m;i++){
		f[i] = f[fl[i]-1] + c[i];
		for(;R && fr[q[R-1]]<i;R--);
		if(R) f[i] = min(f[i] , f[q[R-1]-1]+c[q[R-1]]);
		LL val = f[i-1] + c[i];	
		if((R && val<f[q[R-1]-1]+c[q[R-1]])|| !R)
			q[R++] = i;
	}
	printf("%lld\n",f[m]);
}
乐播投屏是一款简单好用、功能强大的专业投屏软件,支持手机投屏电视、手机投电脑、电脑投电视等多种投屏方式。 多端兼容与跨网投屏:支持手机、平板、电脑等多种设备之间的自由组合投屏,且无需连接 WiFi,通过跨屏技术打破网络限制,扫一扫即可投屏。 广泛的应用支持:支持 10000+APP 投屏,包括综合视频、网盘与浏览器、美韩剧、斗鱼、虎牙等直播平台,还能将央视、湖南卫视等各大卫视的直播内容一键投屏。 高清流畅投屏体验:腾讯独家智能音画调校技术,支持 4K 高清画质、240Hz 超高帧率,低延迟不卡顿,能为用户提供更高清、流畅的视觉享受。 会议办公功能强大:拥有全球唯一的 “超级投屏空间”,扫码即投,无需安装。支持多人共享投屏、远程协作批注,PPT、Excel、视频等文件都能流畅展示,还具备企业级安全加密,保障会议资料不泄露。 多人互动功能:支持多人投屏,邀请好友加入投屏互动,远程也可加入。同时具备一屏多显、语音互动功能,支持多人连麦,实时语音交流。 文件支持全面:支持 PPT、PDF、Word、Excel 等办公文件,以及视频、图片等多种类型文件的投屏,还支持网盘直投,无需下载和转格式。 特色功能丰富:投屏时可同步录制投屏画面,部分版本还支持通过触控屏或电视端外接鼠标反控电脑,以及在投屏过程中用画笔实时标注等功能。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值