題目描述:對於乙個\(n\)個點\(m\)條邊的無向連通圖,每條邊有兩個邊權\(a_i,b_i\),求使\((\sum a_i)\times (\sum b_i)\)最小的生成樹。
資料範圍:\(n\le 200,m\le 10000,a_i,b_i\le 255\)
這題是一道非常妙的計算幾何題目。
我們對於每個生成樹,用\((\sum a_i,\sum b_i)\)這個二維平面上的點來表示它,那麼就是求所有點中橫座標乘縱座標的最小值。
畫畫圖就可以發現,答案只有可能在下凸包上,為什麼呢?
但是生成樹可能有很多個,怎麼得到下凸包上的點呢?
為什麼呢?因為\(a,b\)兩個點必定在下凸包上面。令\(w_i=a_i\)或\(b_i\)用最小生成樹求\(a,b\)。
為什麼呢?因為\(c\)點必定在下凸包上面,否則它不是最大的點。我們發現
\[\begin
-\fracs_&=\overrightarrow\times \overrightarrow \\
&=(x_b-x_a)(y_c-y_a)-(y_b-y_a)(x_c-x_a) \\
&=((x_a-x_b)y_a-(y_a-y_b)x_a)+(x_b-x_a)y_c-(y_b-y_a)x_c
\end
\]令\(w_i=(x_b-x_a)b_i-(y_b-y_a)a_i\)就會得到\(c\)
這樣就可以求出下凸包上所有的點。那什麼時候終止遞迴呢?當然就是\(c\)不存在,或者說求出的\(c\)在\(ab\)上方。
時間複雜度為\(o(km\log m)\),其中\(k\)為下凸包上點的個數,在隨機資料下不會很大。
#include#define rint register int
using namespace std;
typedef long long ll;
const int n = 10003;
int n, m, a[n], b[n], fa[n];
struct point
inline point operator - (const point &o) const ;}
} a, b, ans(1e9, 1e9);
inline ll cross(point a, point b)
struct edge
} e[n];
inline int getfa(int x)
inline point kruskal()
} ll ans = (ll) ans.x * ans.y, res = (ll) res.x * res.y;
if(ans > res || ans == res && ans.x > res.x) ans = res;
return res;
}inline void solve(point a, point b)
int main()
最小乘積 基本型
資源限制 時間限制 1.0s 記憶體限制 512.0mb 問題描述 給兩組數,各n個。請調整每組數的排列順序,使得兩組資料相同下標元素對應相乘,然後相加的和最小。要求程式輸出這個最小值。例如兩組數分別為 1 3 5和 2 4 1 那麼對應乘積取和的最小值應為 5 4 3 2 1 1 25 輸入格式 ...
最小乘積 基本型
給兩組數,各n個。請調整每組數的排列順序,使得兩組資料相同下標元素對應相乘,然後相加的和最小。要求程式輸出這個最小值。例如兩組數分別為 1 3 5和 2 4 1 那麼對應乘積取和的最小值應為 5 4 3 2 1 1 25 第乙個行乙個數t表示資料組數。後面每組資料,先讀入乙個n,接下來兩行每行n個數...
演算法訓練53 最小乘積
問題描述 給兩組數,各n個。請調整每組數的排列順序,使得兩組資料相同下標元素對應相乘,然後相加的和最小。要求程式輸出這個最小值。例如兩組數分別為 1 3 5和 2 4 1 那麼對應乘積取和的最小值應為 5 4 3 2 1 1 25 輸入格式 第乙個行乙個數t表示資料組數。後面每組資料,先讀入乙個n,...