笔试题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

相关文章

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",若

poj 2135 有流量限制的最小费用最大流

题意: 农场里有n块地,其中约翰的家在1号地,二n号地有个很大的仓库。 农场有M条道路(双向),道路i连接着ai号地和bi号地,长度为ci。 约翰希望按照从家里出发,经过若干块地后到达仓库,然后再返回家中的顺序带朋友参观。 如果要求往返不能经过同一条路两次,求参观路线总长度的最小值。 解析: 如果只考虑去或者回的情况,问题只不过是无向图中两点之间的最短路问题。 但是现在要去要回

poj 3422 有流量限制的最小费用流 反用求最大 + 拆点

题意: 给一个n*n(50 * 50) 的数字迷宫,从左上点开始走,走到右下点。 每次只能往右移一格,或者往下移一格。 每个格子,第一次到达时可以获得格子对应的数字作为奖励,再次到达则没有奖励。 问走k次这个迷宫,最大能获得多少奖励。 解析: 拆点,拿样例来说明: 3 2 1 2 3 0 2 1 1 4 2 3*3的数字迷宫,走两次最大能获得多少奖励。 将每个点拆成两个

poj 2195 bfs+有流量限制的最小费用流

题意: 给一张n * m(100 * 100)的图,图中” . " 代表空地, “ M ” 代表人, “ H ” 代表家。 现在,要你安排每个人从他所在的地方移动到家里,每移动一格的消耗是1,求最小的消耗。 人可以移动到家的那一格但是不进去。 解析: 先用bfs搞出每个M与每个H的距离。 然后就是网络流的建图过程了,先抽象出源点s和汇点t。 令源点与每个人相连,容量为1,费用为