笔试题3 -- dd爱框框(滑动窗口内值加和大于x的最小区间)

2024-04-18 19:12

本文主要是介绍笔试题3 -- dd爱框框(滑动窗口内值加和大于x的最小区间),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

爱框框(滑动窗口内值加和大于x的最小区间)

文章目录

  • 爱框框(滑动窗口内值加和大于x的最小区间)
    • 读懂题目
    • 方案一(暴力--超时)
      • 1. 利用 multimap 实现
      • 2. 利用 priority_queue 实现
    • 方案二(优化--滑动窗口)
    • 总结

题目链接: dd爱框框_牛客网

题目描述

读入n,x,给出n个数a[1],a[2],……,a[n],求最小的区间[l, r],使 a[l]+a[l+1]+……+a[r] ≥ x,若存在相同长度区间,输出最小的那个

输入描述

  • 第一行两个数,n(1≤n≤10000000), x(1≤x≤10000)
  • 第二行n个数a[i] (1≤a[i]≤1000)

输出描述

输出符合条件l,r (保证有解)

读懂题目

注意点:

  • 题中描述为 “n个数a[1],a[2],……,a[n]“、“求最小的区间[l, r]” ,而我们多数情况下无论数据存储还是区间下标表示都是从0开始,这里需要从1开始记录下标。
  • 输入描述中n个数中所有值都大于0,否则优化算法(滑动窗口)不可用。

方案一(暴力–超时)

1. 利用 multimap 实现

// 利用 multiset:全部案例超时
class Less_set
{
public:bool operator()(const pair<int, int>& a, const pair<int, int>& b) const{return (a.second - a.first) <= (b.second - b.first);}
};int main()
{int n = 0, x = 0;cin >> n >> x;vector<int> v(n, 0);for(int i = 0; i < n; i++){cin >> v[i];}// pair存储区间multiset<pair<int, int>, Less_set> mt_set;for(int i = 0; i < n; i++){int sum = 0;for(int j = i; j < n; j++){sum += v[j];if(sum >= x){mt_set.insert( pair<int, int>(i, j) );break;}}}cout << mt_set.begin()->first << " " << mt_set.begin()->second << endl;

提交截图:

在这里插入图片描述

可以看到代码逻辑没有问题,但是时间复杂度过高,无法通过全部案例。

时间复杂度分析:

使用了两层循环,外层循环遍历每个元素作为区间的起点,内层循环寻找满足条件的区间终点。

对于每个起点,最坏情况下可能需要遍历到数组的末尾,因此内层循环的时间复杂度是 O(n)。由于需要对每个起点执行内层循环,外层循环的时间复杂度也是 O(n)。

因此,总的时间复杂度是 O(n^2)

2. 利用 priority_queue 实现

// 利用 priority_queue:超时(通过率20%)
class Greater_pq
{
public:bool operator()(const pair<int, int>& a,const pair<int, int>& b) const{return (a.second - a.first) > (b.second - b.first) ||(((a.second - a.first) == (b.second - b.first)) &&a.second > b.second);}
};int main()
{int n = 0, x = 0;cin >> n >> x;vector<size_t> v(n, 0);for(int i = 0; i < n; i++){cin >> v[i];}// first:区间内值加和,pair存储区间 second:凑数priority_queue<pair<int, int>,vector<pair<int, int>>, Greater_pq> pq;for(int i = 0; i < n; i++){int sum = 0;for(int j = i; j < n; j++){sum += v[j];if(sum >= x){pq.push( pair<int, int>(i, j));break;}}}cout << pq.top().first +1 << " "<< pq.top().second +1 << endl;
}

提交截图:

在这里插入图片描述

通过了 20% 的测试案例,其他案例由于时间复杂度过高依然没有通过。

时间复杂度分析:

这个方案与 multiset 类似,也是使用了两层循环。但是,由于 priority_queue 的插入操作是 O(log n),每次插入新的区间时都需要 O(log n) 的时间。

因此,总的时间复杂度是 O(n^2 * log n),这比 multiset 实现更慢,因为每次插入都增加了额外的 log n 时间。

方案二(优化–滑动窗口)

对于题目中给出的测试案例,我们有如下双指针法(滑动窗口)实现对数组的O(n)时间复杂度遍历即可得到最小区间的下标:

在这里插入图片描述

代码示例:

// 利用 同向双指针/滑动窗口 实现(通过率100%)
int main() 
{int N = 10000000;int n, x;int retLen = N;cin >> n >> x;vector<int> v(n + 1, 0);for (int i = 1; i < n; i++)     // 注意题目的输出示例下标从1开始{cin >> v[i];}int retLeft = 0, retRight = 0;      // 用来记录最终的最小区间左右两端点int l = 1, r = 1;       // 同向双指针int sum = 0;while (r < n) {// 进窗口sum += v[r];while (sum >= x)    // 判断出窗口条件{if (r - l + 1 < retLen)     // 出现满足出窗口条件的更小区间,更新窗口长度{retLeft = l;retRight = r;retLen = r - l + 1;}// 出窗口sum -= v[l++];}r++;}cout << retLeft << " " << retRight << endl;return 0;
}

提交截图:

在这里插入图片描述

时间复杂度分析:

  • 使用了双指针技术,其中一个指针表示区间的起点,另一个指针表示区间的终点。

  • 这两个指针从数组的起始位置开始,向右移动直到找到满足条件的区间。当区间的和大于等于 x 时,左指针向右移动以缩小区间。

  • 这种方法确保了每个元素最多被访问两次(一次被右指针访问,一次被左指针访问),因此时间复杂度是 O(2n),简化后是 O(n)


总结

​ 对于这道题我们一共尝试了三种不同的方法来解决数据量过大、对时间复杂度要求高的问题,虽然方案1中两种方法分别使用 multimappriority_queue 来辅助存储遍历数组过程中的值,但是时间复杂度是 O(n^2)O(n^2 * log n),这在数据量大时会导致超时。而这里使用的滑动窗口可以很好的解决重复遍历的浪费,分别使用两个指针控制访问数组的节奏,不走回头路,最大程度的实现了提升算法效率。

这篇关于笔试题3 -- dd爱框框(滑动窗口内值加和大于x的最小区间)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/915622

相关文章

Linux使用dd命令来复制和转换数据的操作方法

《Linux使用dd命令来复制和转换数据的操作方法》Linux中的dd命令是一个功能强大的数据复制和转换实用程序,它以较低级别运行,通常用于创建可启动的USB驱动器、克隆磁盘和生成随机数据等任务,本文... 目录简介功能和能力语法常用选项示例用法基础用法创建可启动www.chinasem.cn的 USB 驱动

bat脚本启动git bash窗口,并执行命令方式

《bat脚本启动gitbash窗口,并执行命令方式》本文介绍了如何在Windows服务器上使用cmd启动jar包时出现乱码的问题,并提供了解决方法——使用GitBash窗口启动并设置编码,通过编写s... 目录一、简介二、使用说明2.1 start.BAT脚本2.2 参数说明2.3 效果总结一、简介某些情

基于Redis有序集合实现滑动窗口限流的步骤

《基于Redis有序集合实现滑动窗口限流的步骤》滑动窗口算法是一种基于时间窗口的限流算法,通过动态地滑动窗口,可以动态调整限流的速率,Redis有序集合可以用来实现滑动窗口限流,本文介绍基于Redis... 滑动窗口算法是一种基于时间窗口的限流算法,它将时间划分为若干个固定大小的窗口,每个窗口内记录了该时间

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

poj 1287 Networking(prim or kruscal最小生成树)

题意给你点与点间距离,求最小生成树。 注意点是,两点之间可能有不同的路,输入的时候选择最小的,和之前有道最短路WA的题目类似。 prim代码: #include<stdio.h>const int MaxN = 51;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int P;int prim(){bool vis[MaxN];

poj 2349 Arctic Network uva 10369(prim or kruscal最小生成树)

题目很麻烦,因为不熟悉最小生成树的算法调试了好久。 感觉网上的题目解释都没说得很清楚,不适合新手。自己写一个。 题意:给你点的坐标,然后两点间可以有两种方式来通信:第一种是卫星通信,第二种是无线电通信。 卫星通信:任何两个有卫星频道的点间都可以直接建立连接,与点间的距离无关; 无线电通信:两个点之间的距离不能超过D,无线电收发器的功率越大,D越大,越昂贵。 计算无线电收发器D

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

poj 1734 (floyd求最小环并打印路径)

题意: 求图中的一个最小环,并打印路径。 解析: ans 保存最小环长度。 一直wa,最后终于找到原因,inf开太大爆掉了。。。 虽然0x3f3f3f3f用memset好用,但是还是有局限性。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#incl

hdu 1102 uva 10397(最小生成树prim)

hdu 1102: 题意: 给一个邻接矩阵,给一些村庄间已经修的路,问最小生成树。 解析: 把已经修的路的权值改为0,套个prim()。 注意prim 最外层循坏为n-1。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstri

poj 2175 最小费用最大流TLE

题意: 一条街上有n个大楼,坐标为xi,yi,bi个人在里面工作。 然后防空洞的坐标为pj,qj,可以容纳cj个人。 从大楼i中的人到防空洞j去避难所需的时间为 abs(xi - pi) + (yi - qi) + 1。 现在设计了一个避难计划,指定从大楼i到防空洞j避难的人数 eij。 判断如果按照原计划进行,所有人避难所用的时间总和是不是最小的。 若是,输出“OPETIMAL",若