军训场V题-双指针(Codeforces Round #898 (Div. 4) F. Money Trees 题解)

军训场V题-双指针( Codeforces Round #898 (Div. 4) F. Money Trees 题解)

1.自我介绍

作为一个刚敲代码没有多久的的小蒟蒻,也是第一次发博客😂,有一些没说好的地方希望各位大佬谅解,如果存在错误的地方也希望大家可以指出,那么话不多说,action!🌟


2.题目

军训训练场 - Virtual Judge
原题目

在这里插入图片描述

3.分析题目

根据观察题目我们不难发现Luca所能得到的子数组的数得满足两个条件

1.高度h[i]能被下一个高度h[i]整除(最后一个数因为后面没有数了,得舍弃)。

2.子数组里的所有a[i]相加不能超过k。

这思路不就来了吗,对数组里的数进行两个条件的判断,如果满足条件子数组个数每次加一,否则子数组个数固定,下一个数返回循环重新判断,直接暴力做。(代码乱写的,但大概意思就是这样)

if(h[j]%h[j+1]==0){ //第一个条件
    for(int k=i;k<=j;k++){ //i为子数组的起始下标
		sum+=a[k];
		if(sum<=m){ //第二个条件
		cnt++; //条件满足后,子数组个数加一
		}else{
         b[l]=cnt; //建立一个数组存储每一个子数组的个数
         l++;
         cnt=0; //不要忘记个数清零
      }
}

当然我们写起来肯定比想起来要难,反正我是没暴力出来😹,就算暴力出来也可能会超时,哎,只能去请教圈圈学长了!


4.题目方法-双指针

其实我们思考一下这题就类似于一种滑动窗口,就像一根辣条,我们每次切中间的一段吃,看那一段切的最长,我们要求的就是那一段的长度,这就需要动态处理区间-双指针
在这里插入图片描述

双指针算法

双指针算法是一种常用的算法技巧,主要用于处理数组和链表等线性结构。它通过使用两个指针(索引位置)来遍历数据结构,从而达到减少时间复杂度的目的。双指针算法有多种变体,包括快慢指针、对撞指针和滑动窗口等。

下面是我从网上找的一段与我们题目类似快慢双指针的题目与代码:

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
int main()
{
    string a; //定义一个字符串a
    getline(cin,a); //忽略空格读入所有字符
    int n=a.size(); //n为a字符串的长度
    for(int i=0;i<n i++) //字符串下标都是从0开始
    {
        int j=i; //j为快字符,i为慢字符
        while(a[j]!=' ') j++; //如果没读到空格,j继续向前移动
        for(int k=i; k<j;k++) cout<<a[k]; //把i到j中间的字符输出
        cout<<endl; //记得换行
        i=j; //循环体执行完后for()中的i才i++即,下一次开始时i就到了上一次空格(位置j)的下一位了 
    }
    return 0;
}

我尽力了,如果大家还没搞懂的话可以双指针算法-CSDN博客看看。


5.题目讲解

从题目着手,我们一个一个条件来。

1.高度h[i]能被下一个高度h[i]整除(最后一个数因为后面没有数了,得舍弃)。

2.子数组里的所有a[i]相加不能超过k。

首先我们先判断第一个条件。

while(j+1<n&&h[j]%h[j+1]==0) j++;

我们可以用一个while循环判断数组有多少满足第一个条件的数,j为满足第一个条件的数的个数,至于j+1<n是因为数组的长度是n并且数组最后一个数我们得舍弃。


其次,我们我们继续从j个数里面筛选出满足条件二的数的个数

int sum=0; //sum为总数的大小
for(int k=i,last=i;k<=j;k++) //k为快指针下标,last为慢指针下标,i为起始下标(初始值),j为终止下标(结束值)
{ 
	sum+=a[k];
	while(sum>m) //如果我们加的数字超过了k值(我定义的m是题目中的k值)
	{ 
	sum-=a[last]; //sum每次减去慢指针下标的数,直到总数小于k值为止
		last++; //快指针向右移动,缩小慢指针与快指针的距离
   }
ans=max(ans,k-last+1); //ans每次循环更新为最长子数组的长度,k-last+1就是两个指针之间的距离
}
i=j; //因为前面的数已经判断过了,下次指针从j后面一个数开始判断

我从示例一中的第一个样例分析一下

t:5 m:12
a:3 2 4 1 8//注意看这一组
h:4 4 2 4 1 

我们最开始让i为0(第一个数4的下标为0,第二个数4为1,第三个数2为2,以此类推),j为0(从0开始计数),前面的4,4,2是满足条件一的,所以j=3.

1.第一次循环sum+a[0]=3,不大于m,进入下一个循环。

快指针 3 2 4

慢指针 3 2 4

2.第二次循环sum+a[1]=5,不大于m,进入下一个循环。

快指针 3 2 4

慢指针 3 2 4

3.第三次循环sum+a[2]=9,不大于m,结束循环。

快指针 3 2 4

慢指针 3 2 4

ans=2-0+1=3

所以输出为3。

示例一中的第二个样例也差不多

注意:但如果进入了while循环,慢指针就向右移动了,反正退出while循环后也是使其满足条件二,再进入max函数。


6.AC代码

累死我了,给大家看看我的AC代码😂

#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
using namespace std;
typedef long long ll;
const int N=2e5+10;
int a[N],h[N];
int main()
{
    IOS;
	int t,n,m;
	cin>>t;
	while(t--){
		cin>>n>>m;
		int ans=0,cnt=0;
		for(int i=0;i<n;i++){
			cin>>a[i];
		}
		for(int j=0;j<n;j++){
			cin>>h[j];
		}
		for(int i=0;i<n;i++){
			int j=i;
			while(j+1<n&&h[j]%h[j+1]==0)j++;
			int sum=0;
			for(int k=i,last=i;k<=j;k++){
				sum+=a[k];
				while(sum>m){
					sum-=a[last];
					last++;
				}
				ans=max(ans,k-last+1);
			}
			i=j;
	}
		cout<<ans<<endl;
	}
	return 0;
}

哎,大差不差吧。

其实敲代码真的很累,但需要大家一起坚持,在此我很感谢圈圈学长,苏希无学姐,肖明阳学长,潜水大佬室友等人长期以来给予我的帮助,让我可以坚持下来,最后☆*: .。. o(≧▽≦)o .。.:*☆😘

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值