1. 程式人生 > >uva 1511 最小生成樹

uva 1511 最小生成樹

ace namespace ref space amp 提前 cto mes tdi

https://vjudge.net/problem/UVA-1151

題意,給出N個點以及二維坐標,可以在任意兩點間建立通路,代價是兩點歐幾裏得距離的平方,同時有q個套餐,套餐x有qx個點,代價是qw,

花費qw就能將這qx個點全部相聯通,套餐可以任意選擇幾種或不選,求將所有的點聯通所要的最小代價。

很容易想到一個暴力做法,遍歷2^q種套餐方案,將這些點提前加入後再跑kruskal,這樣的復雜度有些高了。

其實簡單證明一下,我們可以先不買套餐跑一遍kruskal,之後保存下來所選的邊。

遍歷所有q集合時發現,對於之前沒有選到的邊,現在加入了幾條免費邊之後,更不會選到(因為僅憑之前選的邊就足以使得這條邊無貢獻),因為這條邊加入後沒有貢獻,不會因為買套餐而變得有貢獻,我們可以提前除去這些邊,然後數據就很小了。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int f[1005];
 4 int getf(int v){return f[v]==v?v:f[v]=getf(f[v]);}
 5 struct Edge
 6 {
 7     int u,v,w;
 8     bool operator<(const Edge& x)const{
 9     return w<x.w;
10     }
11 }e1[500005],e2[1005];
12 int x[1005],y[1005],cost[15];
13 int
main() 14 { 15 int T,N,Q,n,m,xx,i,s,j,k; 16 //freopen("in.txt","r",stdin); 17 cin>>T; 18 while(T--){vector<int> G[10]; 19 int m1=0,m2=0; 20 cin>>N>>Q; 21 for(i=0;i<Q;++i){ 22 cin>>n>>cost[i]; 23 while(n--){
24 cin>>xx; 25 G[i].push_back(xx); 26 } 27 } 28 for(i=1;i<=N;++i) 29 { 30 cin>>x[i]>>y[i]; 31 for(j=1;j<i;++j) 32 { 33 e1[m1].u=i; 34 e1[m1].v=j; 35 e1[m1++].w=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]); 36 } 37 } 38 for(i=0;i<=N;++i) f[i]=i; 39 sort(e1,e1+m1); 40 int ans=0; 41 s=0; 42 for(i=0;s<N-1&&i<m1;++i) 43 { 44 int fu=getf(e1[i].u),fv=getf(e1[i].v); 45 if(fv!=fu){ 46 e2[m2++]=e1[i]; 47 s++; 48 f[fv]=fu; 49 ans+=e1[i].w; 50 } 51 } 52 for(i=0;i<(1<<Q)-1;++i) 53 { 54 for(j=0;j<=N;++j) f[j]=j; 55 int sum=0; s=0; 56 for(j=0;j<Q;++j) 57 { 58 if(i&(1<<j)){ 59 sum+=cost[j]; 60 for(int k=0;k<G[j].size()-1;++k){ 61 int fu=getf(G[j][k]),fv=getf(G[j][k+1]); 62 if(fu!=fv){ 63 f[fv]=fu; 64 s++; 65 } 66 } 67 } 68 } 69 for(j=0;j<m2&&s<N-1;j++) 70 { 71 int fu=getf(e2[j].u),fv=getf(e2[j].v); 72 if(fu!=fv){ 73 s++; 74 f[fv]=fu; 75 sum+=e2[j].w; 76 } 77 } 78 ans=min(ans,sum); 79 } 80 cout<<ans<<endl; 81 if(T) cout<<endl; 82 83 } 84 return 0; 85 }

uva 1511 最小生成樹