POJ 3061

xiaoxiao2025-04-19  16

 本题题意:

         有个长度为n的数组,问是否存在区间能让区间内部数字和大于k (要求区间尽可能短)

第一行输入 n k

之后输入数组

最终输出最短的区间长度

这道题我们可以用二分法解决,时间大概是 O(nlogn)即一次全部遍历和一次二分搜索

有了之前几道题的竟然。。这道解决的也更加快了建议先自己想一下

 

 

 

 

 

代码如下

 

 

 

 

 

#include<cstdio>   #include<algorithm>   #include<cstring>      using namespace std;      int a[1e8 + 10];      int n, s;      void slove(){       int sum = 0;       for (int i = 0; i < n; i++) {  //记录一个前缀和         sum += a[i];           a[i] = sum;       }          int res = n + 1;       for (int i = 0; a[i] + s <= a[n - 1]; i++) {           int tmp = lower_bound(a + i, a + n, a[i] + s) - a - i;  //调用函数 // lower_bound 是一个封装好的函数,会返回里搜索值最近的较大的数          res = min(res, tmp);       }       if (res == n + 1)printf("0\n");       else printf("%d\n", res);   }      int main() {       int t;       scanf("%d", &t);       while (t--) {           memset(a, 0, sizeof(a));           scanf("%d%d", &n, &s);           for (int i = 0; i < n; i++) scanf("%d", a[i] );           slove();       }       return 0;   }  

当然 还有一种更加优秀的算法 我们叫它 尺取法(因为这种算法很像一种虫子蠕动的感觉,貌似因为尺蠖这种虫子音译的原因)

想象我们构造一个滑动区间(类似于一个可以推动的玻璃门)这个区间内的数字之后如果大于 s 那么这个区间就是成立的然后记录区间长度并且比较。至于怎么去确保这个区间滑动同时里边数字和大于 s 我们就采用那种虫子蠕动的思想:

     上边界持续增长 ,直到大于S

     下边界开始收缩 ,直到小于S

最后记录 每个 Up-Down 的值 比较出最大 就是这种思想了。

#include <iostream> #include <cstdio> #include <algorithm> using namespace std; int s,e; int t,n,k; const int maxn = 1e8+5; int num[maxn]; int main() { cin>>t; while(t--) { cin>>n>>k; for(int i=1;i<=n;i++) { cin>>num[i]; } s=e=1; int sum=0; int res=n+1; for(;;)//构造一个死循环 { while(e<=n&&sum<k)//开始把上边界增长 //就像虫子蠕动时头先向前伸出去 { sum+=num[e]; e++; } if(sum<k)break;//如果走到了头就结束 res=min(res,e-s);//记录最短区间 sum-=num[s++];//然后增长下边界 //像虫子蠕动的时候尾部再向回收缩 } if(res>n)res=0; cout<<res<<endl; } return 0; }

 

转载请注明原文地址: https://www.6miu.com/read-5028652.html

最新回复(0)