【計蒜客習題】聖誕樹
阿新 • • 發佈:2018-08-22
clu 簡單 continue 先來 運算符 opera 整數 node 最短路問題 接下來輸入一行,輸入 n 個整數,依次表示每個結點的權重。
接下來輸入 m 行,每行輸入 3 個正整數a,b,c(1≤a,b,≤n,1≤c≤10,000),表示結點 a 和結點 b 之間有一條權值為 c 的邊可供造樹選擇。
輸出格式
輸出一行,如果構造不出這樣的樹,請輸出No Answer,否則輸出一個整數,表示造樹的最小價值。
樣例輸入
4 4
10 20 30 40
1 2 3
2 3 2
1 3 5
2 4 1
樣例輸出
370
=cntj*we(cnt是j所在子樹的結點個數),我們可以用單源最短路算法求出所有結點到樹根的最短路,所有結點最短路之和就是答案。為什麽呢?題目要求構造一棵樹,那麽考慮從樹根分別走到各個結點的路徑長度之和,則<i,j>的貢獻就是cntj*we,因為每要走到j的子樹中的一個點就要經過一次<i,j>,而這剛好是邊權的定義。那麽如何最小化整棵樹的邊權之和呢?其實就是最小化樹根到每個結點的路徑長度之和,如果樹根確定,只需以樹根為源點,跑一遍單源最短路,然後將各個結點的最短路累加起來,如果樹根不確定,就需要對每個樹根都求一遍到其他結點最短路之和,取最小值。回到原題,每個結點都有權值,其實也很好想,每條邊對於答案的貢獻都擴大了,比如某一結點的權值為w,先只考慮到這個點的路徑,都相當於由原來只經過一次變為經過w次,也就是說,可以把權值為w的點看成沒有權值的w個在相同位置的點,在統計答案時,只需將ans+=d[i]修改為ans+=w[i]*d[i]。
問題描述
聖誕節快到了,蒜頭君準備做一棵大聖誕樹。
這棵樹被表示成一組被編號的結點和一些邊的集合,樹的結點從 1 到 n 編號,樹的根永遠是 1。每個結點都有一個自身特有的數值,稱為它的權重,各個結點的權重可能不同。對於一棵做完的樹來說,每條邊都有一個價值 ve,若設這條邊 e 連接結點 i 和結點 j,且 i 為 j的父結點(根是最老的祖先),則該邊的價值ve=sj*we,sj表示結點 j 的所有子孫及它自己的權重之和,we表示邊 e 的權值。
現在蒜頭君想造一棵樹,他有 m 條邊可以選擇,使得樹上所有邊的總價值最小,並且所有的點都在樹上,因為蒜頭君喜歡大樹。
輸入格式
第一行輸入兩個整數 n 和 m(0≤n,m≤50,000),表示結點總數和可供選擇的邊數。
接下來輸入 m 行,每行輸入 3 個正整數a,b,c(1≤a,b,≤n,1≤c≤10,000),表示結點 a 和結點 b 之間有一條權值為 c 的邊可供造樹選擇。
輸出格式
輸出一行,如果構造不出這樣的樹,請輸出No Answer,否則輸出一個整數,表示造樹的最小價值。
樣例輸入
4 4
10 20 30 40
1 2 3
2 3 2
1 3 5
2 4 1
樣例輸出
370
其實這裏就牽扯到最短路的一類問題,這類問題看似是生成樹問題但其實是最短路問題,原因就在於邊權的定義方式。先來想一種簡單的情況,點的邊權為1,或者說點沒有邊權,對於<i,j>(i是j的父親),ve
1 #include<cstdio> 2 #include<cctype> 3 #include<cstring> 4 #include<queue> 5 using namespace std; 6 inline int get_num() { //讀入優化 7 int num; 8 char c; 9 while((c=getchar())==‘\n‘||c==‘ ‘||c==‘\r‘); 10 num=c-‘0‘; 11 while(isdigit(c=getchar())) num=num*10+c-‘0‘; 12 return num; 13 } 14 void put_num(int i) { //輸出優化 15 if(i>9) put_num(i/10); 16 putchar(i%10+‘0‘); 17 } 18 const int maxn=5e4+5,maxm=1e5+5,inf=0x3f3f3f3f; 19 int n,m,head[maxn],eid,d[maxn],vis[maxn],nw[maxn]; 20 long long ans; //避免溢出 21 struct edge { //鄰接表存儲 22 int v,w,next; 23 edge(int v=0,int w=-1):v(v),w(w) {} 24 } E[maxm]; 25 void init() { //記得初始化 26 memset(head,-1,sizeof(head)); 27 memset(d,inf,sizeof(d)); 28 } 29 void insert(int u,int v,int w) { 30 E[eid]=edge(v,w); 31 E[eid].next=head[u]; 32 head[u]=eid++; 33 } 34 struct node { //自定義結構體保存結點 35 int n,s; 36 node(int n,int s):n(n),s(s) {} 37 bool operator < (const node& rhs) const { //重載小於運算符,使得最短路小的先出隊 38 return s>rhs.s; 39 } 40 }; 41 priority_queue<node> q; 42 void dijkstra(int s) { 43 d[s]=0; 44 q.push(node(s,0)); 45 while(!q.empty()) { 46 int u=q.top().n; 47 q.pop(); 48 if(vis[u]) continue; 49 vis[u]=1; 50 for(int p=head[u];p+1;p=E[p].next) { 51 int v=E[p].v; 52 if(d[v]>d[u]+E[p].w) { 53 d[v]=d[u]+E[p].w; 54 q.push(node(v,d[v])); 55 } 56 } 57 } 58 } 59 int main() { 60 n=get_num(); 61 m=get_num(); 62 for(int i=1;i<=n;++i) nw[i]=get_num(); 63 init(); 64 int a,b,c; 65 for(int i=1;i<=m;++i) { 66 a=get_num(); 67 b=get_num(); 68 c=get_num(); 69 insert(a,b,c); //註意插入的應是雙向邊 70 insert(b,a,c); 71 } 72 dijkstra(1); 73 for(int i=1;i<=n;++i) { 74 if(d[i]==inf) { 75 printf("No Answer"); 76 return 0; 77 } 78 ans+=nw[i]*d[i]; 79 } 80 printf("%lld",ans); 81 return 0; 82 }AC代碼
【計蒜客習題】聖誕樹