UVa 1606 Amphiphilic Carbon Molecules 題解
難度:β
建議用時:40 min
實際用時:1 h
題目:??
代碼:??
這題我看了劉汝佳大神的代碼,在上面改了幾個變量的名稱,方便理解。
這是一道簡單的幾何題(不是真正的集合題,更可以說是一個坐標題),所以長話短說了。
題目告訴我們有一些隨機分布的點,要我們用一個隔板把平面分開,然後往兩邊分別倒入水和丙酮來溶解掉這些點。
然而這些點有的只能溶解在水裏,有的只能溶解到丙酮裏。
任務是找到最大的溶解的點的數量。
這題建模就是按照劉汝佳大神的方法,一個隔板分開平面,要使左側黑點數加右側白點數的和最大。找到最大值。
具體的算法就不討論了,書上有。
這裏說一下大神的代碼裏的細節。
首先弄一個結構體,存點的相對坐標與相對角。
相對誰呢?相對枚舉的原點。
for (int pivot = 0; pivot < n; pivot++)
以這個枚舉的點為原點建立坐標系,表示每個點的坐標和與 x 軸正半軸的夾角。
for (int p = 0; p < n; p++) { if (p == pivot) continue; rlt_pos[tot].x = abs_pos[p].x - abs_pos[pivot].x; rlt_pos[tot].y = abs_pos[p].y - abs_pos[pivot].y; if (color[p] == 1) { rlt_pos[tot].x = -rlt_pos[tot].x; rlt_pos[tot].y = - rlt_pos[tot].y; } rlt_pos[tot].rad = atan2(rlt_pos[tot].y, rlt_pos[tot].x); tot++; }
這裏我把大神的代碼裏面的一些變量名稱改了一下,便於我自己理解。
註意到大神有一個方便的做法。他把右側的黑點當作左側的白點,然後下面只統計左側的點(不管顏色)有幾個(而不是先統計左側的白點又統計右側的黑點)。這樣統計時更快更便捷。
然後把每個點按相對夾角排序,這樣可以保證一個一個掃描。
接下來就是重點了。掃描。
這個掃描的步驟我看了半天才明白。
其大致想法是:對每一個分割線,統計左邊點的個數,然後讓掃描到的點標號不變,改變分割點,繼續從上次掃描的地方開始掃描,計數。
這樣的好處是,不用每次改變分割線後重新從分割線開始掃描。前面的復雜度是 n^2,優化後是 n。
1 int L = 0, R = 0, cnt = 2; // 初始時記掃描點為 L,分割點為 R。 2 while (R < tot) { 3 if (L == R) { L = (L+1)%tot; cnt++; } // 如果掃描點與分割點重合,計數加一 4 while (L != R && left(rlt_pos[L], rlt_pos[R])) { L = (L+1)%tot; cnt++; } // 掃描 5 cnt--; // 減掉上次的分割點 6 R++; // 分割點改變 7 ans = max(ans, cnt); 8 }
這裏比較難想的是第 3 行和第 5 行。
不難發現,第 3 行只在第一次掃描時執行。因為之後掃描點與分割點不可能重合了,這是因為我們要求掃描點必須要在分割點左邊。
即使在某次掃描時掃描點剛好轉到了分割點(然後退出第 4 行),然而分割點每次加 1,這樣掃描點依然追不上分割點。
第 5 行。這裏轉完以後統計時要減 1,減的是上次的分割點。然而對於第一次掃描是不存在“上一個”分割點的,所以 cnt 的初始值設為 2。cnt 代表當前確定的點數。cnt = 1 是原點。
大神的代碼讓我看的花方。
2018-02-03
UVa 1606 Amphiphilic Carbon Molecules 題解