【問題描述】
皮特現在是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,約翰家門外就是第乙個池塘,所以他到第乙個池塘是不用花時間的。約翰可以任選若干個池塘由近到遠地垂釣,並且...