博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
尺取法
阅读量:5279 次
发布时间:2019-06-14

本文共 3741 字,大约阅读时间需要 12 分钟。

今天学了个高级东西——尺取法。(这句话在很多大佬看来是个笑话)。

以下摘自《挑战程序设计竞赛(第二版)》

尺取法通常指对数组保存一对下标(起点和终点),然后根据实际情况交替推进两个端点直到得到答案的方法。这种操作很像是尺蠖(日文中称为尺取虫)爬行的方式故得名。

举几个栗子:

题意是给你一个正整数数列,一个整数 \(S\),问和不小于 \(S\) 的连续子序列的最小长度,无解输出 \(0\)
尺取法入门题。
题目保证 \(a_i > 0\),所以右端点具有单调性,故可以用尺取法。
按照尺取法,设定前后指针 \(s=0\)\(t=0\),然后将 \(t\) 向前移动,判断 \(s\) ~ \(t\) 之间的区间是否满足要求,如果小于 \(S\)\(t\) 继续移动,直到和大于等于 \(S\) 为止。此时将 \(t\) 向前移动。反复进行该过程,期间记录长度,并更新长度的最小值。复杂度为 \(O(n)\)

#include 
using namespace std;#define db double#define ll long long#define RG registerinline int gi(){ RG int ret; RG bool flag; RG char ch; ret=0, flag=true, ch=getchar(); while (ch < '0' || ch > '9') ch == '-' ? flag=false : 0, ch=getchar(); while (ch >= '0' && ch <= '9') ret=(ret<<3)+(ret<<1)+ch-'0', ch=getchar(); return flag ? ret : -ret;}const db pi = acos(-1.0);const int N = 1e5+5, inf = 1<<30;int a[N];inline void work(){ int l,r,i,n,lim,s,ans; n=gi(), lim=gi(); for (i=1; i<=n; ++i) a[i]=gi(); l=r=1, s=a[l], ans=inf; while (r <= n) { while (s < lim && r <= n) s+=a[++r]; if (s < lim) break; ans=min(ans,r-l+1), s-=a[l++]; } ans < inf ? printf("%d\n",ans) :puts("0");}int main(){ // freopen("sub.in","r",stdin); // freopen("sub.out","w",stdout); int t; t=gi(); while (t--) work(); return 0;}

题目不解释了。
首先,喻队长教育我:

“他坐标范围这么大,肯定要离散化,所以交点一定可以在端点上。”

傻逼的我想了30秒,发现好有道理:

判断是否有交是左端点取 \(max\),右端点取 \(min\)

于是只需要每次判断是否有一个区间的一个端点被覆盖 \(m\) 次即可。

剩下的部分就是贪心了。
把区间按长度排序即可。然后用尺取法,每次用线段树维护一下所有点被覆盖次数的 \(max\) 即可。
至于为什么可以一直加,是因为这个区间就算不在 \(h\) 的要选的 \(m\) 个区间内,它选了也不会对答案造成影响,因为后面肯定还有必须要选的区间 \(t\),即当前点要选的第 \(m\) 个区间。(这个有点像最大子序列和的思想)

#include 
using namespace std;#define db double#define ll long long#define RG registerinline int gi(){ RG int ret; RG bool flag; RG char ch; ret=0, flag=true, ch=getchar(); while (ch < '0' || ch > '9') ch == '-' ? flag=false : 0, ch=getchar(); while (ch >= '0' && ch <= '9') ret=(ret<<3)+(ret<<1)+ch-'0', ch=getchar(); return flag ? ret : -ret;}const db pi = acos(-1.0);const int N = 1e6+5, inf = (1ll<<31)-10;struct ran{ int l,r,len; inline bool operator <(const ran &R) const { return len < R.len; }}r[N];int b[N<<1],tr[N<<2],lz[N<<2];inline void down(RG int o){ if (!lz[o]) return; RG int ls,rs; ls=o<<1, rs=ls|1; lz[ls]+=lz[o], lz[rs]+=lz[o]; tr[ls]+=lz[o], tr[rs]+=lz[o]; lz[o]=0;}inline void update(RG int o,RG int l,RG int r,RG int s,RG int t,RG int w){ if (l >= s && r <= t) { tr[o]+=w, lz[o]+=w; return; } down(o); RG int mid,ls,rs; mid=(l+r)>>1, ls=o<<1, rs=ls|1; if (s <= mid) update(ls,l,mid,s,t,w); if (t > mid) update(rs,mid+1,r,s,t,w); tr[o]=max(tr[ls],tr[rs]);}int main(){ // freopen("ran.in","r",stdin); // freopen("ran.out","w",stdout); int n,m; RG int h,t,cnt,i,ans; n=gi(), m=gi(); cnt=0; for (i=1; i<=n; ++i) { r[i].l=gi(), r[i].r=gi(), r[i].len=r[i].r-r[i].l; b[++cnt]=r[i].l, b[++cnt]=r[i].r; } sort(r+1,r+n+1); sort(b+1,b+cnt+1); cnt=unique(b+1,b+cnt+1)-b-1; for (i=1; i<=n; ++i) { r[i].l=lower_bound(b+1,b+cnt+1,r[i].l)-b; r[i].r=lower_bound(b+1,b+cnt+1,r[i].r)-b; } h=t=1, ans=inf; while (true) { while (tr[1] < m && t <= n) update(1,1,cnt,r[t].l,r[t].r,1), t++; if (tr[1] < m) break; ans=min(ans,r[t-1].len-r[h].len); update(1,1,cnt,r[h].l,r[h].r,-1), h++; } ans < inf ? printf("%d\n",ans) : puts("-1"); return 0;}

转载于:https://www.cnblogs.com/y142857/p/7588396.html

你可能感兴趣的文章
浅说 apache setenvif_module模块
查看>>
MySQL--数据插入
查看>>
重新学习python系列(二)? WTF?
查看>>
shell脚本统计文件中单词的个数
查看>>
SPCE061A学习笔记
查看>>
sql 函数
查看>>
hdu 2807 The Shortest Path 矩阵
查看>>
熟悉项目需求,要知道产品增删修改了哪些内容,才会更快更准确的在该项目入手。...
查看>>
JavaScript 变量
查看>>
java实用类
查看>>
smarty模板自定义变量
查看>>
研究称90%的癌症由非健康生活习惯导致
查看>>
命令行启动Win7系统操作部分功能
查看>>
排序sort (一)
查看>>
Parrot虚拟机
查看>>
Teamcenter10 step-by-step installation in Linux env-Oracle Server Patch
查看>>
Struts2学习(三)
查看>>
Callable和Runnable和FutureTask
查看>>
GitHub 多人协作开发 三种方式:
查看>>
文本域添加编辑器
查看>>