2 4 1 1 20 1 2 30 200 2 80 200 1 100 3 1 1 20 1 2 30 2 2 40
65.00 70.00
题意:有n个点,现在要求出一个生成树,每个点都有权值,现在可以让生成树里面的一条边的花费为0,不过要让这条边连接的两点的权值尽可能的大,让A/B的结果为最大,A表示这条边连接的两点的权值之和,B表示生成树所有边的权值之和减去A的这条边。
思路:遍历所有的边,不过遍历是建立在先建出最小生成树的基础上,也就是说先建出最小生成树,如果两个点的边在最小生成树上就直接ans=max(ans,(a[i].z+a[j].z)/(sum-tu[i][j]));如果不在最小生成树上,那么假设我们有这条边,那么必然在已经建好的最小生成树上有一条边要去掉,我们取最优解去掉连接该边成环之后的最大权值边(不包括添加的这条边),那么ans=max(ans,(a[i].z+a[j].z)/(sum-len[i][j])), len[i][j]数组存的就是连接i和j使最小生成树成环之后的环上的除ij之外的最大权值边。
注意:这题特别无奈的是刚开始用的kruskal算法,百度了一下kruskal与prim的时间差别,稠密prim比kcuskal快的不止一下,稀疏图kruskal更快。结果刚开始死活tle,心有不甘,若有人有优化办法望告知博主。
下面是tle代码(kruskal):
#include <iostream> #include <stdio.h> #include <stdlib.h> #include<string.h> #include<algorithm> #include<math.h> #include<queue> using namespace std; typedef long long ll; const int N=1010; double tu[N][N],len[N][N]; bool vis[N][N]; vector <int>g[N]; int n,f[N]; struct data { int x,y,p; }a[N]; struct hh { int a,b; double w; }p[N*N/2]; inline double dis(int i,int j) { return sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y)); } bool cmp(hh a,hh b) { return a.w<b.w; } int fa(int x) { if(x!=f[x]) return f[x]=fa(f[x]); return f[x]; } int main() { int t; scanf("%d",&t); while(t--) { memset(tu,0,sizeof(tu)); memset(len,0,sizeof(len)); memset(vis,0,sizeof(vis)); scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].p),f[i]=i; int bj=0; for(int i=0;i<n;i++) for(int j=i+1;j<n;j++) tu[i][j]=tu[j][i]=dis(i,j),p[bj].a=i,p[bj].b=j,p[bj++].w=tu[i][j]; int k=0; sort(p,p+bj,cmp); double sum=0; for(int i=0;i<n;i++) g[i].push_back(i); for(int i=0;i<bj;i++) { if(k==n-1)break; int x1=fa(p[i].a),x2=fa(p[i].b); if(x1!=x2) { vis[p[i].a][p[i].b]=vis[p[i].b][p[i].a]=1; sum+=tu[p[i].a][p[i].b]; int l=g[x1].size(),ll=g[x2].size(); for(int j=0;j<l;j++) for(int k=0;k<ll;k++) len[g[x1][j]][g[x2][k]]=len[g[x2][k]][g[x1][j]]=tu[p[i].a][p[i].b]; f[x1]=x2; int l1=g[x1].size(); for(int j=0;j<l1;j++) g[x2].push_back(g[x1][j]); g[x1].clear(); } } double ans=-1; for(int i=0;i<n;i++) for(int j=i+1;j<n;j++) if(vis[i][j]) ans=max(ans,(double)(a[i].p+a[j].p)/(sum-tu[i][j])); else ans=max(ans,(double)(a[i].p+a[j].p)/(sum-len[i][j])); printf("%.2f\n",ans); } return 0; }
ac代码(prim):
#include <iostream> #include <stdio.h> #include <stdlib.h> #include<string.h> #include<algorithm> #include<math.h> #include<queue> using namespace std; typedef long long ll; const int N=1010; const int maxn=1010; const double inf=1e14; struct note { int x,y,z; }a[maxn]; double tu[maxn][maxn],dis[maxn]; int pre[maxn],n; int flag[maxn][maxn],vis[maxn]; double len[maxn][maxn]; double prim(int u) { double sum=0; memset(flag,0,sizeof(flag)); memset(vis,0,sizeof(vis)); memset(len,0,sizeof(len)); for(int i=1; i<=n; i++) { dis[i]=tu[u][i]; pre[i]=u; } vis[u]=1; for(int i=1; i<n; i++) { double minn=inf; int v=-1; for(int j=1; j<=n; j++) { if(!vis[j]&&dis[j]<minn) { v=j; minn=dis[j]; } } if(v!=-1) { sum+=dis[v]; flag[v][pre[v]]=flag[pre[v]][v]=1; vis[v]=1; for(int k=1; k<=n; k++) { if(vis[k]&&k!=v) { len[v][k]=len[k][v]=max(len[k][pre[v]],dis[v]); } if(!vis[k]&&tu[v][k]<dis[k]) { dis[k]=tu[v][k]; pre[k]=v; } } } } return sum; } double lenth(int i,int j) { return sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y)); } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d",&n); for(int i=1; i<=n; i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z); for(int i=1; i<= n; i++) for(int j=i+1; j<=n; j++) tu[i][j]=tu[j][i]=lenth(i,j); double sum=prim(1); double ans=-1; for(int i=1; i<=n; i++) for(int j=i+1; j<=n; j++) if(flag[i][j]) ans=max(ans,(a[i].z+a[j].z)/(sum-tu[i][j])); else ans=max(ans,(a[i].z+a[j].z)/(sum-len[i][j])); printf("%.2lf\n",ans); } return 0; }