NOIP复赛复习14尺取法与折半枚举Word文件下载.docx

上传人:b****3 文档编号:17150694 上传时间:2022-11-28 格式:DOCX 页数:12 大小:19KB
下载 相关 举报
NOIP复赛复习14尺取法与折半枚举Word文件下载.docx_第1页
第1页 / 共12页
NOIP复赛复习14尺取法与折半枚举Word文件下载.docx_第2页
第2页 / 共12页
NOIP复赛复习14尺取法与折半枚举Word文件下载.docx_第3页
第3页 / 共12页
NOIP复赛复习14尺取法与折半枚举Word文件下载.docx_第4页
第4页 / 共12页
NOIP复赛复习14尺取法与折半枚举Word文件下载.docx_第5页
第5页 / 共12页
点击查看更多>>
下载资源
资源描述

NOIP复赛复习14尺取法与折半枚举Word文件下载.docx

《NOIP复赛复习14尺取法与折半枚举Word文件下载.docx》由会员分享,可在线阅读,更多相关《NOIP复赛复习14尺取法与折半枚举Word文件下载.docx(12页珍藏版)》请在冰豆网上搜索。

NOIP复赛复习14尺取法与折半枚举Word文件下载.docx

S

所以从as+1开始总和最初超过S的连续子序列如果是as+1+...+at′−1的话,则必然有t≤t′。

用下面的图来解释比较清晰:

#include<

iostream>

cstdio>

cmath>

algorithm>

#definesfscanf

#definepfprintf

usingnamespacestd;

constintMaxn=100010;

intT,n,s;

intsum[Maxn];

intmain()

{

inta;

sf("

%d"

&

T);

while(T--)

{

inttail=-1,head=-1;

%d%d"

n,&

s);

for(inti=0;

i<

n;

i++)

a);

if(i==0)sum[i]=a;

elsesum[i]=sum[i-1]+a;

if(tail==-1andsum[i]>

=s)

tail=i;

}

if(tail==-1)

pf("

0\n"

);

continue;

intMin=n;

while(head<

tail)

if(sum[tail]-sum[head+1]>

=s)

head++;

Min=min(Min,tail-head);

elseif(tail<

n-1)tail++;

else

break;

%d\n"

Min);

return0;

}

POJ3320

题意:

一本书有P页,每一页都一个知识点,求去最少的连续页数覆盖所有的知识点。

分析:

和上面的题一样的思路,如果一个区间的子区间满足条件,那么在区间推进到该处时,右端点会固定,左端点会向右移动到其子区间,且其子区间会是更短的,只是需要存储所选取的区间的知识点的数量,那么使用map进行映射以快速判断是否所选取的页数是否覆盖了所有的知识点。

cstring>

set>

map>

#defineMAX1000010 

#defineLLlonglong 

#defineINF0x3f3f3f3f 

inta[MAX];

map<

int,int>

cnt;

set<

int>

t;

intp,ans=INF,st,en,sum;

intmain() 

scanf("

&

p);

for(inti=0;

i<

p;

i++)scanf("

a+i),t.insert(a[i]);

intnum=t.size();

while

(1){ 

while(en<

p&

&

sum<

num) 

if(cnt[a[en++]]++==0)sum++;

if(sum<

num)break;

 

ans=min(ans,en-st);

if(--cnt[a[st++]]==0)sum--;

printf("

ans);

POJ2566

给定一个数组和一个值t,求一个子区间使得其和的绝对值与t的差值最小,如果存在多个,任意解都可行。

明显,借用第一题的思路,既然要找到一个子区间使得和最接近t的话,那么不断地找比当前区间的和更大的区间,如果区间和已经大于等于t了,那么不需要在去找更大的区间了,因为其和与t的差值更大,然后区间左端点向右移动推进即可。

所以,首先根据计算出所有的区间和,排序之后按照上面的思路求解即可。

#defineMAX100010 

typedefpair<

LL,int>

LLa[MAX],t,ans,tmp,b;

intn,k,l,u,st,en;

psum[MAX];

LLmyabs(LLx) 

returnx>

=0?

x:

-x;

while(scanf("

%d%d"

k),n+k){ 

sum[0]=p(0,0);

for(inti=1;

=n;

i++){ 

%I64d"

a+i);

sum[i]=p(sum[i-1].first+a[i],i);

sort(sum,sum+1+n);

while(k--){ 

t);

tmp=INF;

st=0,en=1;

while(en<

=n){ 

b=sum[en].first-sum[st].first;

if(myabs(t-b)<

tmp){ 

tmp=myabs(t-b);

ans=b;

l=sum[st].second;

u=sum[en].second;

if(b>

t)st++;

elseif(b<

t)en++;

elsebreak;

if(st==en)en++;

if(u<

l)swap(u,l);

%I64d%d%d\n"

ans,l+1,u);

总结:

尺取法的模型便是这样:

根据区间的特征交替推进左右端点求解问题,其高效的原因在于避免了大量的无效枚举,其区间枚举都是根据区间特征有方向的枚举,如果胡乱使用尺取法的话会使得枚举量减少,因而很大可能会错误,所以关键的一步是进行问题的分析!

二、折半枚举 

折半枚举不是一般的双向搜索,当问题的规模较大,无法枚举所有元素的组合,但能够枚举一半元素的组合,此时将问题拆成两半后分别枚举,再合并它们的结果这一方法往往非常有效。

POJ2785

给定各有n个整数的四个数列A,B,C,D。

从每个数列中各取一个数,使四个数的和为0.当一个数列中有多个相同数字时,把它们作为不同的数字看待。

求出这样的组合的个数。

1≤n≤4000

|(数字的值)|≤228

n=6

A={-45,-41,-36,-36,26,-32}

B={22,-27,53,30,-38,-54}

C={42,56,-37,-75,-10,-6}

D={-16,30,77,-46,62,45}

如果全部枚举,则有n4种可能性。

时间复杂度通不过。

因此可以进行折半枚举,计算A,B之间的组合,共有n2种情况,同样的,C,D之间也有n2种情况。

在取出A,B组合中的一组组合(a+b)时,为了使和为0,去查找C,D组合中满足a+b+c+d=0的组合(c+d),这个查找可以用二分查找来实现,因此最后的复杂度是O(n2logn)

string>

typedeflonglongLL;

constintMaxn=4010;

intA[Maxn],B[Maxn],C[Maxn],D[Maxn];

intAB[Maxn*Maxn],CD[Maxn*Maxn];

intn;

LLsolved(intval)

intl=0,r=n*n-1;

LLans;

while(l<

=r)

intmid=(l+r)/2;

if(CD[mid]<

=val)

l=mid+1;

r=mid-1;

ans=r;

l=0,r=n*n-1;

val)

ans=ans-r;

returnans;

while(~sf("

n))

i++)

sf("

%d%d%d%d"

A[i],&

B[i],&

C[i],&

D[i]);

intcnt=0;

for(intj=0;

j<

j++)

AB[cnt]=A[i]+B[j],CD[cnt++]=C[i]+D[j];

sort(CD,CD+cnt);

LLans=0;

ans+=solved(0-AB[i]);

%lld\n"

ans);

POJ3977 

给你一个含n(n<

=35)个数的数组,让你在数组中选出一个非空子集,使其元素和的绝对值最小,输出子集元素的个数以及元素和的绝对值,若两个子集元素和相等,输出元素个数小的那个。

思路:

如果直接暴力枚举,复杂度O(2^n),n为35时会超时,故可以考虑折半枚举,利用二进制将和以及元素个数存在两个结构体数组中,先预判两个结构体是否满足题意,再将其中一个元素和取相反数后排序,因为总元素和越接近零越好,再二分查找即可,用lower_bound时考虑查找到的下标和他前一个下标,比较元素和以及元素个数,不断更新即可。

structZ{

longlongintx;

inty;

booloperator<

(constZ&

b)const 

if(x!

=b.x)

returnx<

b.x;

returny<

b.y;

}a[300005],b[300005];

longlong 

intc[40];

longlongintabs1(longlongintx){

if(x<

0)

return-x;

returnx;

intmain(){

while((cin>

>

n)&

n!

=0){

for(inti=0;

i<

300005;

a[i].x=a[i].y=b[i].x=b[i].y=0;

longlongintsum=1e17;

intans=40;

n;

cin>

c[i];

intn1=n/2;

1<

<

n1;

i++){

for(intj=0;

j<

j++){

if(i>

j&

1&

(i!

=0||j!

=0)){

a[i-1].x+=c[j];

a[i-1].y++;

intn2=n-n1;

(1<

n2);

n2;

b[i-1].x+=c[j+n1];

b[i-1].y++;

n1)-1;

if(abs1(a[i].x)<

sum){

sum=abs1(a[i].x);

ans=a[i].y;

elseif(abs1(a[i].x)==sum&

a[i].y<

ans){

for(inti=0;

a[i].x=-a[i].x;

n2)-1;

if(abs1(b[i].x)<

sum=abs1(b[i].x);

ans=b[i].y;

elseif(abs1(b[i].x)==sum&

b[i].y<

sort(a,a+(1<

n1)-1);

sort(b,b+(1<

n2)-1);

intt=lower_bound(b,b+(1<

n2)-1,a[i])-b;

if(t>

0){

if(abs1(b[t-1].x-a[i].x)<

sum=abs1(b[t-1].x-a[i].x);

ans=b[t-1].y+a[i].y;

elseif(abs1(b[t-1].x-a[i].x)==sum&

b[t-1].y+a[i].y<

if(t<

n2)-1){

if(abs1(b[t].x-a[i].x)<

sum=abs1(b[t].x-a[i].x);

ans=b[t].y+a[i].y;

elseif(abs1(b[t].x-a[i].x)==sum&

b[t].y+a[i].y<

cout<

"

ans<

endl;

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 医药卫生 > 中医中药

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1