UVALive 5713 Qin Shi Huang's National Road System(次小生成树)
题意:对于已知的网络构建道路,使城市两两之间能够互相到达。其中一条道路是可以免费修建的,问需要修建的总长度B与免费修建的道路所连接的两城市的人口之和A的比值A/B最大是多少。
因为是求A/B的最大值,自然A越大,B越小越好。B的最小值是可以用最小生成树算法求解的,但是,由于免费修建一条道路,使得B值<最小生成树的权值和cnt。
于是,就要考虑究竟选择哪条边作为免费修建?只考虑生成树上的边还是全部边都要考虑?仔细想一下,就会发现任何一条边都存在这样的可能性。而A/B的值同时收A、B的影响,即B可以稍微大一点,只要A增大的倍数更大,那么A/B就会出现一个更优解。
至此,选择枚举每一条边(u,v)作为可能免费修建的边。当然,若它在最小生成树上,那么B==cnt-边权;若它不在最小生成树上,那么加上该条边相当于在树形结构上构造了一个环,那么减去环上任何一条边(当然不能是新加的这条边),又构成一棵树。当删除的是原树上u,v两点唯一路径上权值最大的一条边时,这棵树就是对应于所加的边(u,v)的“次小生成树”(这里的次小不是真正的次小)。为什么一定是当前次小呢?由kruskal算法可知,这是通过贪心构造出的一棵树,新加上的边必然是环上的最大值(否则就不会是最小生成树了),而不在环上的边可以保证最小,所以通过如上构造,得到了一棵确定选择边(u,v)后的最小生成树,也是原图的一颗次小生成树(究竟是不是真的是次小,要比较完全部的“次小生成树”才能得到,并且注意次小生成树不唯一)。
用prim算法实现,记录(u,v)两两之间的路径上的最大值:每次记录即将加入生成树的点v与已加入的点之间的最大值,f[v]=max{f[u],w(u,v)},u是v的父亲。
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<vector> 5 #include<algorithm> 6 #define clr(a,m) memset(a,m,sizeof(a)) 7 #define rep(i,a,b) for(int i=a;i<=b;i++) 8 using namespace std; 9 10 const int MAXN=1111; 11 const double INF =1e9; 12 13 struct Point{ 14 int c; 15 double x,y; 16 }p[MAXN]; 17 18 double mp[MAXN][MAXN],f[MAXN][MAXN]; 19 20 double d[MAXN]; 21 int vis[MAXN],fa[MAXN]; 22 23 double prim(int n) 24 { 25 vector<int>q; 26 double cnt=0; 27 28 clr(vis,0); 29 rep(i,1,n) 30 d[i]=INF; 31 d[1]=0; 32 fa[1]=1; 33 rep(i,1,n){ 34 int x; 35 double m=INF; 36 rep(y,1,n) 37 if(!vis[y]&&d[y]<m) 38 m=d[x=y]; 39 vis[x]=true; 40 cnt+=mp[fa[x]][x]; 41 42 int sz=q.size(); 43 rep(j,0,sz-1){ 44 f[q[j]][x]=f[x][q[j]]=max(f[q[j]][fa[x]],mp[fa[x]][x]); 45 } 46 q.push_back(x); 47 48 rep(y,1,n) 49 if(!vis[y]&&mp[x][y]<d[y]){ 50 d[y]=mp[x][y]; 51 fa[y]=x; 52 } 53 } 54 return cnt; 55 } 56 57 void print(int n,double cnt) 58 { 59 double m=0; 60 rep(i,1,n) 61 rep(j,i+1,n){ 62 double s=cnt-f[i][j]; 63 double t=p[i].c+p[j].c; 64 m=max(m,t/s); 65 } 66 printf("%.2f\n",m); 67 } 68 69 int main() 70 { 71 int T,n; 72 scanf("%d",&T); 73 while(T--) 74 { 75 scanf("%d",&n); 76 rep(i,1,n) 77 scanf("%lf%lf%d",&p[i].x,&p[i].y,&p[i].c); 78 rep(i,1,n){ 79 mp[i][i]=0; 80 rep(j,i+1,n) 81 mp[i][j]=mp[j][i]=sqrt((p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y)); 82 } 83 double cnt=prim(n); 84 print(n,cnt); 85 } 86 return 0; 87 }