尺取法

尺取法,顾名思义就是用尺子取得某段连续的区间。在算法上我们常常用一对下标来衡量这个“尺子”的大小,而这个“尺子”左右长度有多长这取决于你对这段连续区间的要求是什么。尺取法在我看来有点像双指针法,因为本质上也是靠两个移动的下标来划分区间的,但是网上的人都特别取名尺取法,而且尺取法有一套比较规范的模板,那我这里也特地记录一下以作学习理解用。

尺取法原理

尺取法用一对下标衡量所选取区间的长度,先不断移动右下标,直到区间内的数据达到你的初步条件后暂停移动,数据初步处理完后,再移动左下标缩小区间以达到你的进一步条件,期间也可以进一步的处理数据,随着区间的减小到你的初步条件不成立后,又接着移动你的右下标,如此循环,直到到达了数组的上限。

(如果你发现左右移动下标,还是无法提取出你想要的数据,你可以先考虑对数组内的数据进行预处理或者仔细斟酌你的条件筛选步骤,这样后你还是发现步骤复杂难以实现,可能尺取法就不适合解决这个问题了)

 

例题:

很久很久以前,有一个古老的王国,这个王国里有n户人家,国王是个很喜欢团结的人,他总是喜欢让他的子民们举行一些活动,来使他们促进团结。这天,他在巡视国家时,发现街上的人走路迈出的步子大小可能不一样。他灵感一现,想到了一个非常有意思且能促进团结的游戏,“两人三足”!!!但是国王喜欢团结,所以他并不想让他的子民分组决出胜负,而是要求每家每户都派出一个人,然后让所有人排成一排,组成一个“m人m+1足”。只要他们能顺利走完一定的路程,国王就会很高兴,然后就会开国库,发粮食!子民很当然很想得到粮食啊,但是他们陷入了困惑,因为每个人的迈出的步子大小是固定的,没法缩小或增大,而且只要有两个人的步子大小差值超过k,那么他们就会集体摔倒,游戏失败。现在他们求助于你,把每家每户的人的步子大小告诉你,让你帮他们确认他们是否可能完成游戏。为什么求助于你呢?因为在这个故事里,你就是他们国家隔壁山上正在修炼的程序员!(滑稽)

两人三足的游戏规则:由两个人团结协作,两人并排站立,一人左腿与另一人右腿的膝盖以下,脚踝以上部分用绳子绑上 比赛在起点处开始出发,至对面标志处折回,返回至起点处,再将绳子解后,交给下一组队员进行比赛,最后以完成时间长短进行排名。——复制自百度百科

输入描述:

第一行两个整数n和k,n(1 <= n <= 100000),表示王国有多少户人家,k(1 <= k <= 1000000000),表示最大步子和步子差值超过k就会集体摔倒;接下来n行,每行5个整数a(1 <= a <= 1000000000),第i个整数代表这户人家第i个人步子的大小。

输出描述:

输出包含一行,若子民们能完成游戏,输出所有可行方案中的最大步子和最小步子的差值的最小值;若不可能完成游戏,输出-1。

 

题目想要从所给的n户人家中选出n个人,使这n个人的步伐间距最小,并且每户人家至少且最多选出一人。那么这道题如果用dfs或者动态规划都比较难想,(dfs复杂度太高,动态规划我想不出来,老菜鸡了),用尺取法恰好合适。

首先对这n户人家的信息预处理,标记每个人来自哪户人家,然后把这5n个人(题目给定每户人家有5个人待选)排成从小到大的数组,再对这个数组使用尺取法,初期不断移动右下标的区间条件是这个区间内必须有来自n户人家的,然后移动左下标,每移动一次左下标就要相应的求一次最大步伐间隔,并且减去对应那户人家的被选进去的人口。由于数组是从小到大的,所以直接用右下标对应人的步伐减去左下标对应人的步伐就是该区间内最大步伐(不用考虑这两个人是否是同一户人家,因为初步筛选条件下,左下标和右下标就不可能是同一户人家)。

代码如下:

 1 #include<iostream>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 class person{
 6     public:
 7         int id,a;
 8         bool operator < (person &x)
 9         {
10             return a<x.a;
11         }
12 }arr[500010];
13 int main()
14 {
15     int n,k,arrlength(0);
16     cin>>n>>k;
17     for(int a=1;a<=n;a++)
18         for(int b=1;b<=5;b++)
19         {
20             arr[++arrlength].id=a;
21             scanf("%d",&arr[arrlength].a);
22         }
23     sort(arr+1,arr+1+arrlength);
24     int l=1,r=1,count(0),mingap(0x3f3f3f3f);
25     int vis[500010];
26     memset(vis,0,sizeof(vis));
27     while(r<=arrlength&&l<=arrlength)
28     {
29         for(;r<=arrlength&&count<n;r++)
30         {
31             if(!vis[arr[r].id])
32                 count++;
33             vis[arr[r].id]++;
34         }
35         if(count==n)
36         {
37             mingap=min(mingap,arr[r-1].a-arr[l].a);
38             if(vis[arr[l].id]==1)
39                 count--;
40             vis[arr[l].id]--;
41             l++;
42         }
43     }
44     if(mingap<=k)
45         cout<<mingap<<endl;
46     else
47         cout<<-1<<endl;
48 }

 

posted @ 2021-01-04 15:27  miyui  阅读(307)  评论(0)    收藏  举报