1. 程式人生 > 實用技巧 >城市規劃 藍橋杯

城市規劃 藍橋杯

最小生成樹變種

題目的意思是:

  棟棟居住在一個繁華的C市中,然而,這個城市的道路大都年久失修。市長準備重新修一些路以方便市民,於是找到了棟棟,希望棟棟能幫助他。

  C市中有n個比較重要的地點,市長希望這些地點重點被考慮。現在可以修一些道路來連線其中的一些地點,每條道路可以連線其中的兩個地點。另外由於C市有一條河從中穿過,也可以在其中的一些地點建設碼頭,所有建了碼頭的地點可以通過河道連線。

  棟棟拿到了允許建設的道路的資訊,包括每條可以建設的道路的花費,以及哪些地點可以建設碼頭和建設碼頭的花費。

  市長希望棟棟給出一個方案,使得任意兩個地點能只通過新修的路或者河道互達,同時花費盡量小。

  輸入的第一行包含兩個整數n, m,分別表示C市中重要地點的個數和可以建設的道路條數。所有地點從1到n依次編號。

  接下來m行,每行三個整數a, b, c,表示可以建設一條從地點a到地點b的道路,花費為c。若c為正,表示建設是花錢的,如果c為負,則表示建設了道路後還可以賺錢(比如建設收費道路)。
  接下來一行,包含n個整數w_1, w_2, …, w_n。如果w_i為正數,則表示在地點i建設碼頭的花費,如果w_i為-1,則表示地點i無法建設碼頭。
  輸入保證至少存在一個方法使得任意兩個地點能只通過新修的路或者河道互達。

思路:

從題目中我們可以看出如果只有路的話直接跑最小生成樹就可以,如果加上河那麼這裡有個妙的想法就是把河也看成一個點,建立碼頭所花費的費用就是邊的費用。所以跑兩遍克魯斯卡爾,不過還有個細節要注意就是如果邊權為負數,這個邊是不但不用花錢還會賺錢,那麼就是必選,多多益善嘛~

所以程式碼如下:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 1e4+10;
const int M = 2e5+10;
int n,m;
struct edge{
    int from;
    int to;
    int c;
    edge(int from,int to,int c){
        this->from=from;
        
this->to=to; this->c=c; } bool operator < (const edge& e)const{ return this->c < e.c; } }; vector<edge> edges; int cost[N]; int fa[N]; int find(int a){ if(a==fa[a]){ return a; } return fa[a]=find(fa[a]); } int krske(){ for(int i=1;i<=n+1;i++)fa[i]=i; sort(edges.begin(),edges.end()); int res=0; int cnt=n-1; for(int i=0;i<edges.size();i++){ int u=edges[i].from; int v=edges[i].to; int c=edges[i].c; int fau=find(u); int fav=find(v); if(fau!=fav){ fa[fau]=fav; res+=c; cnt--; } else if(c<0) res+=c; } if(cnt==0){ return res; } return 0x3f3f3f3f; } int main(){ cin>>n>>m; for(int i=1;i<=n;i++){ fa[i]=i; } for(int i=0;i<m;i++){ int a,b,c; cin>>a>>b>>c; edges.push_back(edge(a,b,c)); } int res1=krske(); for(int i=1;i<=n;i++){ int wi; cin>>wi; if(wi==-1){ continue; } edges.push_back(edge(i,n+1,wi)); } n++; int res2=krske(); cout<<min(res1,res2)<<endl; }