POJ 3061 Subsequence(二分/尺取法)
2015-09-02 19:46
525 查看
题目链接:
http://poj.org/problem?id=3061
解题思路:
题目大意:
给定长度为n的整列整数a[0],a[1],……a[n-1],以及整数S,求出总和不小于S的连续子序列的长度的最小值。
由于所有的元素都大于零,如果子序列[s,t)满足a[s]+...+a[t-1]>=S,那么对于任何t <t'一定有a[s]+...a[t'-1]>=S。此外对于区间[s,t)上的总和来说如果令s
um[i]=a[0]+a[1]+...+a[i-1]那么a[s]+a[s-1]+...a[t-1]=sum[t]-sum[s],因此预先以O(n)的时间计算好sum的话,就可以以O(1)的时
间计算区间上的总和。这样一来,子序列的
起点s确定以后,便可以用二分搜索快速地确定使序列和不小于S的结尾t的最小值。
AC代码(二分法):
这个时间复杂度是O(nlogn),虽然足以解决这个问题,但我们还可以更高效地解决。我们设以a[s]开始的总和最初大于S时的连续子序列为a[s]+a[s+1]+……a[t-1],这时,
a[s+1]+a[s+1]+……a[t-2]<a[s]+a[s+1]+……a[t-2]<S,所以如果从a[s+1]开始总和最初超过S的连续子序列是a[s+1]+……a[t‘-1],则t<=t'。
故可以设计如下算法:
(1)初始s=t=sum=0
(2)只要依然有sum<S,就不断将sum加上a[t],并且t=t+1;
(3)如果(2)中无法满足sum>=s则终止,否则ans=min(ans,t-s);
(4)将sum减去a[s],s增加1然后回到(2).
对于这个算法,因此t最多变化n次,因此只需O(n)的复杂度就可以求解这个问题了。
5 1 3 5 10 7 4 9 2 8
↓
5 1 3 5 10 7 4 9 2 8
↓
5 1 3 5 10 7 4 9 2 8
↓
5 1 3 5 10 7 4 9 2 8
↓
5 1 3 5 10 7 4 9 2 8
↓
5 1 3 5 10 7 4 9 2 8
↓
5 1 3 5 10 7 4 9 2 8
↓
5 1 3 5 10 7 4 9 2 8
AC代码(尺取法):
http://poj.org/problem?id=3061
解题思路:
题目大意:
给定长度为n的整列整数a[0],a[1],……a[n-1],以及整数S,求出总和不小于S的连续子序列的长度的最小值。
由于所有的元素都大于零,如果子序列[s,t)满足a[s]+...+a[t-1]>=S,那么对于任何t <t'一定有a[s]+...a[t'-1]>=S。此外对于区间[s,t)上的总和来说如果令s
um[i]=a[0]+a[1]+...+a[i-1]那么a[s]+a[s-1]+...a[t-1]=sum[t]-sum[s],因此预先以O(n)的时间计算好sum的话,就可以以O(1)的时
间计算区间上的总和。这样一来,子序列的
起点s确定以后,便可以用二分搜索快速地确定使序列和不小于S的结尾t的最小值。
AC代码(二分法):
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 100005; int n,s; int a[maxn]; int sum[maxn+1]; void solve(){ //计算sum for(int i = 0; i < n; i++) sum[i+1] = sum[i]+a[i]; if(sum < s){ printf("0\n"); return ; } int res = n; for(int i = 0; sum[i] + s <= sum ;i++){ //利用二分搜索求出t int t = lower_bound(sum+i,sum+n,sum[i]+s)-sum; res = min(res,t-i); } printf("%d\n",res); } int main(){ int T; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&s); for(int i = 0; i < n; i++) scanf("%d",&a[i]); solve(); } return 0; }
这个时间复杂度是O(nlogn),虽然足以解决这个问题,但我们还可以更高效地解决。我们设以a[s]开始的总和最初大于S时的连续子序列为a[s]+a[s+1]+……a[t-1],这时,
a[s+1]+a[s+1]+……a[t-2]<a[s]+a[s+1]+……a[t-2]<S,所以如果从a[s+1]开始总和最初超过S的连续子序列是a[s+1]+……a[t‘-1],则t<=t'。
故可以设计如下算法:
(1)初始s=t=sum=0
(2)只要依然有sum<S,就不断将sum加上a[t],并且t=t+1;
(3)如果(2)中无法满足sum>=s则终止,否则ans=min(ans,t-s);
(4)将sum减去a[s],s增加1然后回到(2).
对于这个算法,因此t最多变化n次,因此只需O(n)的复杂度就可以求解这个问题了。
5 1 3 5 10 7 4 9 2 8
↓
5 1 3 5 10 7 4 9 2 8
↓
5 1 3 5 10 7 4 9 2 8
↓
5 1 3 5 10 7 4 9 2 8
↓
5 1 3 5 10 7 4 9 2 8
↓
5 1 3 5 10 7 4 9 2 8
↓
5 1 3 5 10 7 4 9 2 8
↓
5 1 3 5 10 7 4 9 2 8
AC代码(尺取法):
#include <iostream> #include <cstdio> using namespace std; int n,S; int a[100005]; void solve(){ int res = n+1; int s = 0,t = 0,sum = 0; while(1){ while(t < n && sum < S){ sum += a[t++]; } if(sum < S) break; res = min(res,t-s); sum -= a[s++]; } if(res > n){ //解不存在 res = 0; } printf("%d\n",res); } int main(){ int T; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&S); for(int i = 0; i < n; i++) scanf("%d",&a[i]); solve(); } return 0; }
相关文章推荐
- 黑马程序员之GUI
- FMDB fmdb-master 在UI中使用OC语法对sqlate数据库处理
- easyUI的tree
- easyUI的tree
- 禁用mysql query cache
- IOS中UISearchController搜索框筛选功能实现
- mongo:Criteria和Query
- UIALertView的基本用法与UIAlertViewDelegate对对话框的事件处理方法
- UISearchBar介绍
- UE4 FBX静态网格物体通道
- Arduino串口的正确打开方式
- iOS开发工具——Reveal:iOS的UI设计分析利器
- UIView详解
- android SystemUI阿拉伯语言下系统图标和通知图标交换位置
- UITextView控件的用法详解
- UE4 FBX Content Pipeline
- UITextField 使用全面解析
- Unique Binary Search Trees
- connection holder is null -- druid-1.0.9
- UIButton使用详解