题目大意:给你n个数字,一个终点数S,求这n个数字(顺序不能改变)中截取k个数字使之和大于或等于这个数S
解题思路:
1. 二分法:
输入数字的时候对设置一个辅助数组进行预处理,即在i位置的时候使这个数组保存着从开头到目前这个区间所有数字的累和,目的是什么呢?当你知道一个区间的头和尾的时候你直接用尾部位置的数减去头部前一个位置的数得到的就是本区间的数字累和。然后就是寻找头部和尾部,用一个循环枚举头的情况,而尾部的情况就可以利用二分查找,其实这个预处理过的数组一定是升序的。复杂度是O(nlogn)。POJ上360ms
2.尺取法:
其实思路很类似,都是寻找这个区间,尺取法则是先初始化头尾在同一个位置,然后把尾部往后挪,知道sum(区间数累和)大于等于S时更新Min(区间长度),然后头部往后挪,在表上演示步骤时就像是一个会走动的区间。在《挑战程序设计竞赛》第二版P146-P150左右有这个算法的四个步骤。和我描述的大同小异。
二分法:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int book[100010],n,S;
int find()
{
int i,t,Min=100011;
for(i=0;S<=book[n]-book[i];i++)
{
t=lower_bound(book+i,book+n+1,book[i]+S)-book-i;
if(t<Min)
Min=t;
}
return Min;
}
int main()
{
int i,j,t;
cin>>t;
while(t--)
{
cin>>n>>S;
memset(book,0,sizeof(book));
for(i=1;i<=n;i++)
{
cin>>book[i];
book[i]+=book[i-1];
}
if(book[n]>=S)
cout<<find()<<endl;
else
cout<<"0"<<endl;
}
return 0;
}
尺取法:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
int book[100010],n,S;
int find()
{
int head,tail,Min=100011,sum;
head=tail=1;
sum=0;
while(1)
{
while(tail<=n && S>sum)
sum+=book[tail++];
if(S>sum)
break;
if(Min>tail-head)
Min=tail-head;
sum-=book[head++];
}
if(Min>n)
Min=0;
return Min;
}
int main()
{
int i,j,t;
cin>>t;
while(t--)
{
cin>>n>>S;
memset(book,0,sizeof(book));
for(i=1;i<=n;i++)
cin>>book[i];
cout<<find()<<endl;
}
return 0;
}
这个也是AC代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
int book[100010],n,S;
int find()
{
int i,head,tail,Min=100011;
head=tail=0;
while(1)
{
while(tail<=n && S>book[tail]-book[head])
tail++;
if(tail>n)
break;
else if(Min>tail-head)
Min=tail-head;
head++;
}
return Min;
}
int main()
{
int i,j,t;
cin>>t;
while(t--)
{
cin>>n>>S;
memset(book,0,sizeof(book));
for(i=1;i<=n;i++)
{
cin>>book[i];
book[i]+=book[i-1];
}
if(book[n]>=S)
cout<<find()<<endl;
else
cout<<"0"<<endl;
}
return 0;
}