Peter算法小课堂—序列切割

讲序列切割之前,先来个铺垫

高手集训

题目描述:

课程表里有连续的n天可以供你选择,每天都有专题课程。其中第i天的专题趣味程度为h[i]。假设你选择了其中连续的若干天,从第l天到第r天。那么,

训练效果 = h[l]*1 + h[l+1]*2 + ... + h[r]*(r-l+1)

随着训练的深入进行,每天的趣味程度会得到更多倍数的效果。

目前有m种训练方案,每种方案由起始时间和结束时间来描述。请对每种方案输出训练效果。

算法分析

这道题目,我们可以死做,然后就会TLE。为了避免TLE,我们引入一种特殊的前缀和。

其中,s[i]为朴素的前缀和,g[i]为编号加权前缀和。

那么,上面为公式的推导。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=100009;
typedef long long ll;
ll n,m,h[N],s[N],g[N];
void input(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>h[i];
	}
}
void BF(){
	for(ll i=1;i<=m;i++){
		ll l,r;
		cin>>l>>r;
		ll ans=0;
		for(ll j=l;j<=r;++j) ans+=h[j]*(j-l+1);
		cout<<ans<<" ";
	}
}
void solve(){
	for(ll i=1;i<=n;i++){
		s[i]=s[i-1]+h[i];
		g[i]=g[i-1]+h[i]*i;
	}
	for(ll i=1;i<=m;++i){
		ll l,r;
		cin>>l>>r;
		ll ans=g[r]-g[l-1]-(s[r]-s[l-1])*(l-1);
		cout<<ans<<" ";
	}
}
int main(){
	freopen("training.in","r",stdin);
	freopen("training.out","w",stdout);
	input();
	if(n<=1000&&m<=1000) BF();
	else solve();
	return 0;
}

这里,我两种算法都放进去了。

防汛指挥

题目描述:

每年夏天,城市的河道都会经历一次汛期的挑战。你作为市长,正在紧锣密鼓的做防汛指挥工作。你所管理的城市有一条中心河道,河道的一侧容易决堤,而这一侧依次排列着n座楼房,编号1到n,其中第i座楼房高度为h[i]。目前你需要将楼房分成若干个相邻连续的区域,由各个区域内部协调防汛资源。若某个连续区域内楼房数量num达到L,则该区域中最矮的 num/L (向下取整) 座楼房会被关闭,其中人员会到区域内其他楼房暂住。若某个连续区域内楼房数量num未到达G,则该区域所有楼房都不可以关闭。通过确定分组管理的方案,本市未关闭的楼房里高度总和最小是多少?

算法分析

首先,这是一道动态规划题。AC动态规划题的心度历程:状态定义→根据样例列表格→找规律→状态转移方程→写代码

定义f[i]表示前i座楼房分组后未关闭的最小高度总和。

给出一组样例:

然后大家列一列表格

/*
f[i]表示前i座楼房分组后未关闭的最小高度总和
n=6,L=3
	i=1 ,2 ,3 ,4 ,5, 6
 h[i]=2  2  5  4  5  1
 f[i]=2  4  7  11 14 15
*/

通过找规律,我们发现

于是,我们的状态转移方程也就呼之欲出了

其中rmq()表示求最小值。

代码

/*
f[i]表示前i座楼房分组后未关闭的最小高度总和
n=6,L=3
	i=1 ,2 ,3 ,4 ,5, 6
 h[i]=2  2  5  4  5  1
 f[i]=2  4  7  11 14 15
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=100009;
ll n,L,h[N],bst[N],q[N],f[N],s[N];
void RMQ(){
	int l=0,r=0;
	for(int i=1;i<=n;i++){
		while(l<r&&i-q[l]>=L) l++;
		while(l<r&&h[i]<h[q[r-1]]) r--;
		q[r++]=i;
		bst[i]=h[q[l]];
	}
}
int main(){
	freopen("flood.in","r",stdin);
	freopen("flood.out","w",stdout);
	cin>>n>>L;
	for(int i=1;i<=n;i++) cin>>h[i];
	for(int i=1;i<=n;i++) s[i]=s[i-1]+h[i];
	RMQ();
	for(int i=1;i<=n;i++){
		if(i<L) f[i]=s[i];
		else f[i]=min(f[i-1]+h[i],f[i-L]+s[i]-s[i-L]-bst[i]);
	}
	cout<<f[n]<<endl;
	return 0;
}

结束

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值