今日頭條2018春招-演算法方向題解
1.P為給定的二維平面整數點集。定義 P 中某點x,如果x滿足 P 中任意點都不在 x 的右上方區域內(橫縱座標都大於x),則稱其為“最大的”。求出所有“最大的”點的集合。(所有點的橫座標和縱座標都不重複, 座標軸範圍在[0, 1e9) 內)
如下圖:實心點為滿足條件的點的集合。請實現程式碼找到集合 P 中的所有 ”最大“ 點的集合並輸出。
第一行輸入點集的個數 N, 接下來 N 行,每行兩個數字代表點的 X 軸和 Y 軸。
對於 50%的資料, 1 <= N <= 10000;
對於 100%的資料, 1 <= N <= 500000;
輸出“最大的” 點集合, 按照 X 軸從小到大的方式輸出,每行兩個數字分別代表點的 X 軸和 Y軸。
輸入例子1:
5
1 2
5 3
4 6
7 5
9 0
輸出例子1:
4 6
7 5
9 0
對於50萬的資料,暴力求解On2肯定超時,連1W的資料都超時,於是選擇溫柔求解~
先對所有點的y座標從大到小排序。對於y座標最大的點,它一定是“最大”點,對於x座標第二大的點,它必須比第一個點的x座標大,才能是“最大”點。以此類推,對於y座標第n大的點,它的x座標必須比前面的點都大。(“前面”指y座標第1~n-1大的點)
如果排好序之後每個數都往前比一遍y座標,複雜度0.5n^2,依舊超時。於是想到設一個maxx,表示前面的數最大的x座標,每次就只用和maxx比一次x座標,不用迴圈比較。複雜度就是On。不過因為排序複雜度是nlogn,所以整體複雜度nlogn~
上程式碼……
#include <iostream> #include <cstdio> #include <algorithm> using namespace std; struct nn{ int x,y; }qq[500005]; bool cmp(nn a,nn b){ return a.y>b.y; } int main(){ int n,ans=0,maxx=-1; cin>>n; for(int i=0;i<n;i++) scanf("%d%d",&qq[i].x,&qq[i].y); sort(qq,qq+n,cmp);//對y座標比較 for(int i=0;i<n;i++) if(qq[i].x>maxx){ printf("%d %d\n",qq[i].x,qq[i].y); maxx=qq[i].x;//更新最大的y座標 } }
二十幾行就搞定了,舒服~
題目要求按x座標從小到大輸出,所以這裡排序是對y從大到小排序,因為只要它是“最大”點,y最大時x就最小。所以我可以直接輸出啦~啦啦啦~
2.給定一個數組序列, 需要求選出一個區間, 使得該區間是所有區間中經過如下計算的值最大的一個:
區間中的最小數 * 區間所有數的和最後程式輸出經過計算後的最大值即可,不需要輸出具體的區間。如給定序列 [6 2 1]則根據上述公式, 可得到所有可以選定各個區間的計算值:
[6] = 6 * 6 = 36;
[2] = 2 * 2 = 4;
[1] = 1 * 1 = 1;
[6,2] = 2 * 8 = 16;
[2,1] = 1 * 3 = 3;
[6, 2, 1] = 1 * 9 = 9;
從上述計算可見選定區間 [6] ,計算值為 36, 則程式輸出為 36。
區間內的所有數字都在[0, 100]的範圍內;
輸入描述:
第一行輸入陣列序列長度n,第二行輸入陣列序列。
對於 50%的資料, 1 <= n <= 10000;
對於 100%的資料, 1 <= n <= 500000
輸出描述:
輸出陣列經過計算後的最大值。
輸入例子1:
3
6 2 1
輸出例子1:
36
又是50萬的資料!這些序列的排列組合方式肯定算不過來了,暴力無解。而且這道題是不能排序的,因為求的是最大區間。我一開始想錯了,直接排序,GG。
於是想到對於每一個數往左找,往右找,找到比他小的數為止。左邊界和右邊界形成一個區間,就只會列出50萬個區間。區間再乘這個數,就是對於這個數來說的最大區間,最後比較每一個數的最大區間,得到答案。
但是如果題目給出極端資料,1、2、3、4……500000這種,每一個數往右找區間邊界都要找到底,時間複雜度直接爆炸。雖然本道題說數字給定1-100的整數,沒有小數,也就是說區間的長度不會那麼長,這樣做肯定不會超時,但我對自己要求高一點~我不能這樣比到底。於是我給上述演算法融合了動態規劃~
上程式碼……
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int a[500002],sum[500002],r[500002],l[500002];
int main(){
int n;
cin>>n;
memset(sum,0,sizeof sum);
for(int i=1;i<=n;i++){
cin>>a[i];
sum[i]=sum[i-1]+a[i];
}
for(int i=1;i<=n;i++){//每一個點往左遍歷,直到遇到比自己小的
int temp=i;
while(temp>1&&a[i]<=a[temp-1])//這裡是<=
temp=l[temp-1];//DP思想
l[i]=temp;
}
for(int i=n;i>=1;i--){//每一個點往右遍歷,直到遇到比自己小的
int temp=i;
while(temp<n&&a[i]<=a[temp+1]) temp=r[temp+1];
r[i]=temp;
}
int ans;
for(int i=1;i<=n;i++)
ans=max(ans,(sum[r[i]]-sum[l[i]-1])*a[i]);
cout<<ans;
}
從最左端開始迴圈,每一個數往左比,如果第二個數比第一個數小,DP思想那一行,直接就等於l【temp-1】,就不用一直比到底了。同理從最右端開始迴圈,往右比,對於1-500000的極端資料複雜度就為On啦~
那個小於等於要注意,我一開始寫的小於,只過了70%資料。因為題目說的是最小數,不小於最小數的數都可以納入這個區間裡~