【洛谷 2021.10.6团队模拟赛】大哥扛纵连【RMQ】【区间DP】

102 篇文章 1 订阅
66 篇文章 1 订阅
本文详细解析了一道使用动态规划和线段树优化的竞赛编程题目,介绍了70分及100分解决方案。70分方案通过预处理区间最大值并计算前缀和来求解,100分方案则进一步优化,利用区间DP和RMQ找到最优面基位置,实现快速查询。
摘要由CSDN通过智能技术生成

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
example4


解题思路

不想叙述关于打线段树搞这题用了一个上午的事,我哭死,最后半小时发现思路错了就X尼玛,然后在线段数的基础上改了一个暴力,还比直接暴力少了30分。

70分(LZH大佬思路):设 s u m [ i ] [ j ] sum[i][j] sum[i][j]表示再i点面基,从j到i所需代价,代价就是i~j中的最大值(RMQ瞎搞就好),预处理出这个 s u m sum sum,然后做一个前缀和,最后还是要枚举面基的地点,然后输出就是 s u m [ i ] [ y ] − s u m [ i ] [ x − 1 ] sum[i][y]-sum[i][x-1] sum[i][y]sum[i][x1]

100分:区间DP+RMQ优化
先70分中的做法一样,做一个RMQ预处理,注意这里要每次存区间最大值所在的位置,后面DP要用到。

f [ i ] [ j ] f[i][j] f[i][j] 表示区间 [ i , j ] [i,j] [i,j]面基的最小代价,转移的话,找出区间 [ i , j ] [i,j] [i,j]中最高的山 k k k
注意区间的枚举顺序需要像区间 d p dp dp 那样,先枚举区间长度,然后枚举左端点。

考虑面基地点:
如果在 k k k 的左边,代价为 f [ l ] [ k − 1 ] + s ∗ ( r − k + 1 ) f[l][k-1]+s*(r-k+1) f[l][k1]+s(rk+1)
如果在 k k k 的右边,代价为 f [ k + 1 ] [ r ] + s ∗ ( k − l + 1 ) f[k+1][r]+s*(k-l+1) f[k+1][r]+s(kl+1)

在这两种方案中取一个最小值即可(因为面基地点肯定不会是在 k 处的)。
f [ l ] [ r ] = m i n ( f [ l ] [ k − 1 ] + s ∗ ( r − k + 1 ) , f [ k + 1 ] [ r ] + s ∗ ( k − l + 1 ) ) ; f[l][r]=min(f[l][k-1]+s*(r-k+1),f[k+1][r]+s*(k-l+1)); f[l][r]=min(f[l][k1]+s(rk+1),f[k+1][r]+s(kl+1));

然后每一次询问都可以O(1)回答了。


代码

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
#define db double
using namespace std;

int n,m,l,r;
ll ans,a[2010],f[2020][2020],lg[2020];

struct c{
	int x,id;
}g[2020][15];

int main() {
//	freopen("1.in","r",stdin);
//	freopen("1.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1; i<=n; i++)
	{
		scanf("%lld",&a[i]);
		g[i][0].x=a[i];
		g[i][0].id=i;
		f[i][i]=a[i];
	}
	lg[0]=-1;
	for(int i=1;i<=n;i++)
		lg[i]=lg[i>>1]+1;
	for(int j=1;j<=lg[n];j++)
	{
		for(int i=1;i+(1<<j)-1<=n;i++)
		{
			g[i][j].x=max(g[i][j-1].x,g[i+(1<<j-1)][j-1].x);
			if(g[i][j-1].x>g[i+(1<<j-1)][j-1].x)
					g[i][j].id=g[i][j-1].id;
			else g[i][j].id=g[i+(1<<j-1)][j-1].id;
			
		}
	}
	for(int j=1;j<=n;j++)
	{
		for(int l=1;l+j-1<=n;l++)
		{
			int r=0,w=0,s=0,k=0;
			r=l+j-1;
			w=lg[r-l+1];
			s=max(g[l][w].x,g[r-(1<<w)+1][w].x);
			if(g[l][w].x>g[r-(1<<w)+1][w].x)
				k=g[l][w].id;
			else k=g[r-(1<<w)+1][w].id;
			f[l][r]=min(f[l][k-1]+s*(r-k+1),f[k+1][r]+s*(k-l+1));
		}
	}

	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&l,&r);
		printf("%d\n",f[l][r]);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值