面試算法總結系列——尺取法的應用,滑動窗口

最近準備筆試的過程中,發現好多算法題,有點套路的感覺,只怪自己平時積累不多,現在只能亡羊補牢了。

關於尺取法的概念我就不介紹了,網上這方面講解很多,主要說一些應用的方面,積累下目前遇到的一些可以用尺取法可以來解決的題目,不定期更新下。
尺取法參考博客

在此說明,我的夢想是進入網易,校招進入網易已經不可能了,我就在這裏立一個誓言,將來一定會進網易
沒想到曾經的網易,現在也因爲"暴力拆遷"事件,而變得風聲鶴唳,網易就不指望了。

尺取法的應用

簡單可以理解爲,所有求連續的一段區間的相關問題(比如求和,求差),都可以考慮下尺取法。注意,尺取法需要保證所有點按順序排列好,只能勇往直前,而不能後退。

尺取法要維護左右兩個點,左邊這個點慢一些,右邊這個點走的快一些,兩個點間的不能超過最大的臨界,否則就要移動點。

1:搜狗校招9月08號筆試題

題意:同一個圓上面有n個點,n個點按照從小到大排列,每個點有一個角度a(0<=a<=360),代表圓心角,問題是讓你求這些點構成的所有劣弧對應最大的圓心角(按劣弧來算,比如,10,193,圓心角就爲177)

思路:這裏就以180爲’臨界’,左右兩個點距離爲’尺’,兩個點都從原點出發,順時針走,如果大於180,左點前進一位,如果小於180,右點向前進一位。因爲圓是循環的,所以左邊這個點必須走遍圓中所有點,才能訪問完所有的最大的劣弧(注意,不需要訪問所有劣弧,只需要訪問所有最大的劣弧,這裏的最大,是指對於某個點來說的最大)。右邊的點在沒走完一圈之前,採用a[right]-a[left]計算,右點走完一圈之後,採用360-a[left]+a[right%n]來計算。
代碼如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
bool exp(double a,double b){
    if(a-b>1e-12)
        return true;
    else
        return false;

}
int main()
{
    int n;
    scanf("%d",&n);
    double a[n+10];
    for(int i=0;i<n;i++){
        scanf("%lf",&a[i]);
    }

    double ans = -1;
    int left=0,right=0;
    while(left<n){
        if(right<n){
            double temp = a[right]-a[left];
            if(exp(temp,180)){
                left++;
            }else{
                ans = max(ans,temp);
                right++;
            }
        }

        else{
            double temp = 360-a[left]+a[right%n];
            if(exp(temp,180)){
                left++;
            }else{
                ans = max(ans,temp);
                right++;
            }
        }
    }
    printf("%.8lf\n",ans);
    return 0;
}
/*
4
10.00000000
180.00000000
183.00000000
198.00000000

3
10.00000000
180.00000000
190.00000000
*/

小優化 :第一次尋找最大尺的時候,可以採用二分法。這個題必須是有序的,如果無序,需要先排序再操作,否則,不滿足變量遞增的效果。

2.最長子區間問題

題意:要求求出最長連續子區間,區間要求所有值加起來,是k的倍數。例如,區間1,2,3,4,5,給出k=5,那麼滿足要求的區間有[1,2,3,4],[2,3],[1,2,3,4,5],顯然[1,2,3,4,5]最長

思路:這個題跟尺取法唯一相同點就是連續問題。需要記錄前綴和。因爲題目要求爲最大,所以需要記錄第一個出現該值的位置,再次出現該值時,說明從第一次出現到當前出現位置,所有數加起來爲k的倍數。

memset(dp,-1,sizeof(dp));
int temp=0,ans=0;
for(int i=0;i<n;i++){
	temp = (temp+a[i])%k;
	if(dp[temp]!=-1){
		len = i-dp[temp];
		ans = max(ans,len);
	}else{
		dp[temp] = i;
	}
}
printf("%d\n",ans);

3.滴滴出行9月10號 xor問題

題意:給你一段區間,求出最大的滿足不重疊連續子區間的個數,子區間要求區間內所有數異或爲0
參考博客Xor(滴滴筆試題)

思路:這裏和上面一道題目很像,不同的是本題要求的操作爲異或。搞清楚一點:從左往右掃,滿足條件的第一個區間,中間肯定沒有子區間滿足條件,所以這個區間一定構成最大子區間個數中的其中一個(即使中間部分可能和後面的連續序列異或後滿足條件,也不在做計算,比如[4,3,2,4,3,2,3,2],[432432]爲子區間[3232]也可以爲子區間,但是一定不會選擇[3232],因爲他所消耗的區間長度比[4,3,2,4,3,2]長,[3,2,3,2]相當於消耗了這一整個序列)。

這裏稍微說一下相似的題目,畢竟找工作,多拓展一點沒壞處。
題目:求最大不相交區間的個數。
這個題採用貪心的算法,要記錄下所有區間的left點和right點,然後根據right點的大小來排序,排序後從左往右找,
##後記:
還有一些尺取法的應用,都可以採用尺取法套路一下,後面再接着更新,如果有錯誤的地方,希望大家不吝賜教,畢竟本人小菜雞

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章