热乎着,昨晚阿里这题真太绝了
前言
大家好,我是bigsai,好久不見甚是想念。
昨晚有個同學參加了阿里的筆試題,筆試完后同學說這次筆試感覺難,跟我說了其中一道題,我看了感覺還是挺有質量的,看著這個難度都是第二題,總共三題感覺還是有難度的(瑟瑟發抖),想著還是和大家分享一下。
描述
一個正m邊形,他想知道多邊形中等腰銳角三角形的數量。(三角形的頂點要在多邊形的頂點上)
不同的三角形的定義:兩個三角形,只要有一個點不在同一個位置上就算做不同的三角形。
數據范圍3-10^7
分析
昨天看到這個問題,覺得還是比較新穎,也有很多細節,但是剛拿到這個問題自己懵逼的畫了好久也才想出來,這里給大家share一下。
瞎折騰問題的描述很簡單,邊為n的正多邊形有多少等腰銳角三角形。
遇到這種問題我們該怎么分析呢?當然是先畫幾個案例分析一下。
這么一看,你可能覺得好像沒啥規律哇。
你可能對奇數的有點眉目:奇數的每個邊直直的就是對應一個點,那么有多少條邊就有多少個等腰銳角三角形?
但是這個顯然是錯誤的,有可能以不同的頂點作為等腰銳角三角形它剛好是個等邊三角形,這樣就會出現重復,然后還有的底邊可能并不是恰好是多邊形的一個邊,而是多邊形多個邊組成底邊(參考上圖的正6邊型)。
并且從這來看奇數邊和偶數邊還是有點區別的:放正來看,奇數的是點對邊,而偶數的是點對點,結構上有些區別,那么有可能奇偶在結果上是有點區別的。
上面都是一個懵懵懂懂的狀態,咱們整理一下有用的信息:
正多邊形,多少邊就是多少個頂點
正多邊形,有軸對稱的特性,等腰銳角三角形,也有軸對稱的特性。
等腰銳角三角形,腰有兩個,而底邊有一個,要么從底邊考慮,要么從頂點考慮,這里底邊如上面的正6邊型甚至更多邊型顯然不容易考慮,但是各個頂點都是多邊形的頂點,所以肯定從頂點考慮。算出一個頂點為等腰銳角頂角的所有三角形乘以頂點數量(如正五邊形)然后減去重復(如正3、正6邊形)的就是總結果了。
奇數偶數分開討論。
偶數情況
我們先用偶數的情況分析,先不考慮重復情況(考慮太多腦子混淆),將 圖形擺一下成這樣:
因為為正多邊形,所以也就相當于各個頂點在圓上,這樣更容易分析是不是銳角,這樣的分析每個點,就很容易看出每個頂點對應多少個銳角了,正6、正8每個頂點都對應一個銳角,其實有的人可能已經看出規律了,就是在直角下方的線都能組成銳角。
為了更清晰的觀察,看下面這個圖更清晰:
其實就是計算直角下面的藍色線的數量,這個直觀一點是所有橫線數量的一半(向下取整),每個橫線由兩個點組成,我們計算一下:
總共n個點,去掉兩個頂點和最底下單個點,n-2個點組成(n-2)/2的線。其中直角下面的占一半就是[(n-2)/2]/2=(n-2)/4個,然后乘以頂點數量n,那么在偶數情況不考慮重復所有等腰銳角三角形數量為:
total=n*((n-2)/4))奇數情況
回了偶數分析,奇數也很簡單,奇數頂點不變,底邊固定,也就是找能夠組成銳角三角形的邊數。
對于橫線數量,如果是偶數個沒疑問下面是銳角(上面多個頂點所以角度比直角小),而奇數個中間那條線同樣也是銳角(如果去掉頂點才在中央),所以這種情況是橫線數量的一半(向上取整),我們計算一下:
總計n個點,一個頂點,n-1個點組成線。那么有(n-1)/2個線,其中組成銳角三角形的一半向上取整那就是(n-1)/2+1,然后乘以頂點數量n,那么在奇數情況不考慮重復所有等腰銳角三角形數量為:
total=n*(((n-1)/2+1)/2))重復三角形
上面考慮了不重復的三角形的情況,那么重復的三角形該怎么考慮了,上面看到正三角形、正6邊型都遇到等邊三角形而導致重復計算。
而正5、正7好像沒有重復的等邊三角形。
我們認真分析一下:等腰銳角三角形三個頂點都在正多邊形的邊上,其實也在一個圓上,如果構成等邊三角形,說明這三個頂點能夠將空間均分分開(也就是頂點、圓可以均勻分成三份)。那么頂點數量 n必須是三的倍數才可以啊!
頂點數量n是3的倍數,那么具體重復了多少呢?
就是看這種等邊三角形每個作為頂點,本來應該有n個,但是每種情況出現了三次,所以只考慮其中1/3作為頂點的等邊三角形才不重復!所以我們要總次數去掉n的(2/3)。
那么總次數:
total-=(n/3)*2;具體代碼
這里代碼有個小坑,n是10^7,那么這個數量級會越界int范圍,需要用long,但是輸入有的人用int計算這么樣代碼:
public?static?void?main(String[]?args)?{Scanner?sc=new?Scanner(System.in);int?n=sc.nextInt();long?total;if(n%2==0)total=n*((n-2)/4);elsetotal=n*(((n-1)/2+1)/2);if(n%3==0){total-=(n/3)*2;}System.out.println(total); }但是還是錯了,有的人還不知道為啥,主要的原因是下面這兩句越界了:
total=n*((n-2)/4); total=n*(((n-1)/2+1)/2);有人說我已經total設成total設成long為啥還是錯的,因為賦值運算符是先計算右側n*((n-2)/4)此時相當于這么一個流程:
int?temp=n*((n-2)/4); long?total=temp;因為計算的數值范圍都是int,所以最后結果也是int已經越界,然后將越界的這個int結果賦值給long范圍的total。
和這個很類似的還有:
double?a=3/2;System.out.println(a);會輸出1.0而不是1.5,如果想要正確計算那么要提前將計算的值轉成double計算:
double?a=(double)?3/2;同樣這個問題正確的代碼為:
public?static?void?main(String[]?args)?{Scanner?sc=new?Scanner(System.in);int?n=sc.nextInt();long?total;if(n%2==0)total=(long)n*((n-2)/4);elsetotal=(long)n*?(((n-1)/2+1)/2);if(n%3==0){total-=(n/3)*2;}System.out.println(total); }總結
這種數學題、或者找規律的題還是偶爾可能碰到,多會一題多長個經驗!當前還是以積累為主。
大家一起加油,有需要的也歡迎一起打卡力扣。
我是bigsai,肝了一本數據結構與算法pdf和一本動態規劃pdf
歡迎添加?「bigsai66」,備注[pdf]領取
原創不易,希望路過彥祖仙女點個贊、再看
總結
以上是生活随笔為你收集整理的热乎着,昨晚阿里这题真太绝了的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 聊聊买卖股票的最佳时机
- 下一篇: json从立地到成佛