1. 程式人生 > >UVa 1606 Amphiphilic Carbon Molecules 題解

UVa 1606 Amphiphilic Carbon Molecules 題解

一道 github con 描點 溶解 想法 ref 討論 上一個

難度:β

建議用時: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 題解