C 心路歷程30 APIO2013 道路費用

2021-08-03 16:28:22 字數 3007 閱讀 8735

【問題描述】

皮特現在是c國最富有的人。

c國共有n個城市(用1~n 編號),現在這些城市由m條雙向道路連線,其中城市1為首都。保證乙個人從城市1出發,經過這些道路可以到達其他的任何乙個城市。當然,所有的這些道路都是要收費的,使用道路i需要向該道路的所有者支付ci的費用。已知所有的ci互不相同。最近c國計畫新建 條道路,毋庸置疑,當然是富豪皮特負責,因而新建的k條道路(也僅有這k條道路)是屬於皮特的。

皮特可以自行決定著k條道路的費用,並且皮特將在明天公布這些費用。兩周以後,c國將在首都舉行盛大的閱兵式,其中共有pi個參與者從城市i出發。大量的參與者將沿著這些道路前往首都。這些人只會沿著乙個選出的道路集合行進,根據乙個古老的習俗,這些道路將由最富有的人,也就是皮特指定,並且皮特將在後天公布選出的集合。同樣根據這個習俗,皮特選出的道路集合必須使所有選出的道路的費用之和最小,並且仍要保證每個城市都可以經選出的道路到達首都。也就是說,選出的道路來自以費用作為相應邊權的最小生成樹。如果有多個這樣的集合,皮特可以任選。

儘管皮特現在是首富,但他依舊想盡辦法斂財。他希望通過控制屬於他的 條道路的費用以及所選取的道路集合來使自己的收入最大化。他明白,他獲得的收益並不只與指定的費用有關,也與通過這條道路的人數有關。準確的說,如果有p 個人經過費用為ci 的道路,那麼道路所有者就會獲得p*ci 的收入。注意,皮特的選擇必須符合習俗。

但是皮特並不夠聰明,於是他來求助你。你現在需要做的就是計算皮特所能得到的最大收入!

【輸入格式】

第一行包含三個由空格隔開的正整數n,m,k 。

接下來的m 行描述最開始的m 條道路。這ui,vi,ci 行每行包含三個空格隔開整數 ,表示在ui 和vi 之間有一條費用為ci 的雙向道路。保證1<=ui,vi<=n ,且當i!=j 時,ci!=cj 。

接下來k 行描述k 條新道路。每行包含兩個空格隔開的整數ai,bi ,表示有一條連線城市ai,bi 的新道路,其費用由皮特決定。保證1<=ai,ni<=n 。

最後一行包含n 個空格隔開的整數,其中第i 個表示pi ,即城市i 中要前往首都的人數。

保證在任意兩個城市之間,最多有一條道路連線(包括新建的道路)。

【輸出格式】

輸出只有一行,包含乙個整數,表示皮特能獲得的最大收入。

【輸入樣例】

5 5 1

3 5 2

1 2 3

2 3 5

2 4 4

4 3 6

1 3

10 20 30 40 50

【輸出樣例】

【樣例解釋】

皮特應將新道路(1,3) 的費用設為5 。在這個費用下,他可以選擇道路(3,5)、(1,3)、(2,4) 和 (1,3)。可以以證明,這樣的收入是最大的。

大意:n個點,點權為pi,m條邊的圖,新建不超過k條道路,每條費用由你決定,一條道路收取的費用為通過人數*邊上的費用。但前提是所選邊構成mst.在所有人都要去1號點集合的情況下,求你的最大收益。  

感覺題解網上其他地方似乎已經很多啦。所以就大概說一下。

暴力列舉1(期望得分20,過k=1的資料)(o(k*m))

將k條邊逐一加入到原圖最小生成樹中,替換原圖上的最大邊。在k>1時不能保證正確。

核心**:

`ll lca(int u,int v)

while(dep[u]!=dep[v])

while(u!=v)

ll a=maxtt;

return a;

}void solve()

sort(e+1,e+1+m);

for(int i=1;i<=m;i++)

memset(fa,0,sizeof(fa));memset(dep,0,sizeof(dep));

dfs(1,0,1);//給lca做準備

for(int i=1;i<=k;i++) minv[i]=inf;

for(int i=1;i<=m;i++)

}memset(fa,0,sizeof(fa));memset(sz,0,sizeof(sz));

dfs2(1,0);

ll ans=0;

for(int i=1;i<=k;i++)

if(vis[i])

ans=max(ans,ans);

}void run(int i,int r)//列舉2^k種可能方案

vis[i]=1;run(i+1,r+1);vis[i]=0;

run(i+1,r);

}

演算法三:

注意到原圖中只有經過k條邊才有可能收錢,即在其他邊中無論怎麼樣移動都無關,即有些點是粘在一起的,與本題所求無關。即可以「縮點」。

怎麼縮點?

在原圖中,優先使用k條邊生成mst,則k條邊將原圖分成k+1個連通塊,每個連通塊即可縮為乙個點。(連通塊中移動不干擾題目,並與k條邊絕對無關)

再求這k+1個點的最小生成樹,即原圖壓縮成只有(k+1)個點,k條邊的新圖。(這張圖在本題所求中是與原圖等價的,故而可以縮點),再通過演算法二求解這張圖即可。

時間複雜度:o(m*logm + 2^k*k*k)

核心**:

void dfs3(int i,int f,int cc)//方便縮點

}void yasuo()

sort(e+1,e+1+m);

for(int i=1;i<=m;i++)

for(int i=1;i<=n;i++)

if(!belong[i]) dfs3(i,0,++cc);

np=0,memset(first,0,sizeof(first));

kruskal2();

}

p.s.

1.在列舉完邊之後的操作中,如需要清空陣列,請清空小陣列(或新開小陣列),原陣列仍然很大,清空很慢!

2.可以剪枝的地方:在列舉2^k可能性時,如加入的k條邊已經與原圖構成環,可以跳出。

3.我自己程式足足寫了250行,看起來就很冗雜+弱雞,就不貼了……

4.感覺還是很有收穫!

C 學習心路歷程

這裡只記錄c 的特色地方,想來本科專攻c 跟圖形程式設計,真心沒想到半路殺出來個unity還特麼用了c 所以嘛,最近惡補這方面的東西,把跟c 相似的地方看看就好,這裡把c 的好東東都記錄下,有一些還挺有逼格的說,希望能有幸成為高手!1 params關鍵字 本關鍵字在於可變長引數的利用,比如 stat...

C 心路歷程27 變數排序

變數排序 問題描述 把不同的變數進行公升序排序用小於操作符命令實現的。例如由a b,b c 和 c d 可以得到公升序序列a,b,c,d 在本題中,你將得到若干條小於操作符命令,形如 a b 的格式,請你確定利用這些命令,能否得到乙個唯一的公升序序列。輸入格式 第1行為整數n,m,n 2 n 26 ...

C 心路歷程37 釣魚(刷表法dp)

問題描述 約翰是個垂釣謎,星期天他決定外出釣魚h小時 1 h 16 約翰家附近共有n個池塘 2 n 25 這些池塘分布在一條直線上,約翰將這些池塘按離家的距離由近到遠編上號,依次為l1,l2,ln,約翰家門外就是第乙個池塘,所以他到第乙個池塘是不用花時間的。約翰可以任選若干個池塘由近到遠地垂釣,並且...