HDU-4081-次小生成树
HDU-4081-次小生成树
题意
给定二维坐标上的 \(n\) 个城市,连接 \(n-1\) 条边,求最大的比率:连接的两个点权和 / 其它路的长度值和。
思路
写题
一眼最小生成树没有问题。大致想明白了,应该是先求一遍生成树,(边的处理 \(O(n^2*log(n^2))\)),然后枚举所有的边,这里要删去的边好像不是很求,如果在建个树,用 \(lca\) 求两点间的最长路径时间复杂度 \(O(n^2 log(n))\),有 \(n^2\) 的点对,求一次 \(logn\) 这样的话,总时间复杂度 \(O(T*n^2 log(n^2))\) ,好像只有 \(1e8\) 应该能卡过。
反正都有 \(n^2\) 了还搞个蛋 \(lca\) ,直接对每个点 \(dfs\) 预处理就好了,真的是,啊啊啊啊。
我来总结一下这道题的优化:(虽然一点优化没有还更好些而且能过):
- 用原始的 \(prim\) ,时间复杂度 \(O(n^2)\)。
- 在 \(prim\) 的过程中预处理出两点间的最长边,时间复杂度 \(O(n^2)\)。
正文: 一眼最小生成树,求出 \(MST\) 之后枚举边,计算时减去预处理(\(dfs\))出的最长边。也就是 \(kruskla\) 求次小生成树的 \(mlogm+n*(n+m)\) 版。
code
#include <bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn = 1e3+10;
constexpr int maxm = 1e6+10;
typedef struct edge
{
int x,y;
double w;
bool operator<(const edge &t)const
{
return w<t.w;
}
}edge;
typedef struct node
{
int t;
double w;
node(int a,double b):t(a),w(b){}
}node;
int n;
edge edges[maxm];
int xi[maxn],yi[maxn],wi[maxn];
int idx;
int fa[maxn];
vector<node> gra[maxn];
double ma[maxn][maxn];
int dep[maxn];
double calc(int i,int j)
{
double dx=xi[i]-xi[j];
double dy=yi[i]-yi[j];
return sqrt(dx*dx+dy*dy);
}
void init()
{
for(int i=1;i<=n;++i)
{
fa[i]=i;
gra[i].clear();
}
}
int finr(int x)
{
return fa[x]==x ? x : fa[x]=finr(fa[x]);
}
void dfs(int u,int p,double d,int st)
{
ma[st][u]=d;
for(auto i : gra[u])
{
int v=i.t;
double w=i.w;
if(v==p) continue;
dfs(v,u,max(d,w),st);
}
}
double kruskal()
{
double ret=0;
init();
sort(edges+1,edges+1+idx);
int cnt=0;
for(int i=1;i<=idx;++i)
{
int x=edges[i].x;
int y=edges[i].y;
int xr=finr(edges[i].x);
int yr=finr(edges[i].y);
double w=edges[i].w;
if(xr!=yr)
{
fa[xr]=yr;
gra[x].emplace_back(node(y,w));
gra[y].emplace_back(node(x,w));
ret+=w;
++cnt;
if(cnt>=n-1)
{
break;
}
}
}
return ret;
}
void solve()
{
idx=0;
scanf("%lld",&n);
for(int i=1;i<=n;++i)
{
scanf("%lld%lld%lld",xi+i,yi+i,wi+i);
for(int j=1;j<i;++j)
{
edges[++idx]={j,i,calc(i,j)};
}
}
double tal=kruskal();
for(int i=1;i<=n;++i)
{
dfs(i,0,0,i);
}
double ans=0;
for(int i=1;i<=idx;++i)
{
double tmp_tal=tal;
int x=edges[i].x;
int y=edges[i].y;
double w=edges[i].w;
tmp_tal-=ma[x][y];
ans=max(ans,(wi[x]+wi[y])/tmp_tal);
}
printf("%.2lf\n",ans);
}
signed main()
{
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif // ONLINE_JUDGE
int t;
scanf("%lld",&t);
while(t--)
{
solve();
}
return 0;
}
优化
用原始的 \(prim\) ,时间复杂度 \(O(n^2)\)。用类似次小生成树的思路在 \(prim\) 中预处理最长边。(这题不用判是否在 MST 中,但别人都写了,所以提一嘴)
原理是 \(u\) 即将加入的时候, \(u\) 的父节点已经加入并求得树上其他点到父节点的最长边,再将其他点到父节点和父节点到 \(u\) 取 \(max\) 即可。
code
#include <bits/stdc++.h>
using namespace std;
#define int long long
constexpr int INF=99999999;
constexpr int maxn=1010;
struct point
{
int x,y;
} p[maxn];
double mp[maxn][maxn];// 邻接矩阵
double dis[maxn];
double path[maxn][maxn];//从i到j的路径上最大边的权值
int wi[maxn];//每个城市的人口数
int pre[maxn];// 树上的父节点
int vis[maxn];
int n;
double Dist(point v1,point v2)
{
return sqrt(double(v1.x-v2.x)*(v1.x-v2.x)+double(v1.y-v2.y)*(v1.y-v2.y));
}
double Prim()
{
double tal=0;
memset(vis, 0, sizeof(vis));
memset(path, 0, sizeof(path));
vis[1]=1;
for(int i=1; i<=n; ++i)
{
dis[i] = mp[1][i];
pre[i] = 1;
}
for(int i=1; i<n; ++i)
{
int u=-1;
for(int j=1; j<=n; ++j)
{
if(!vis[j])
{
if(u==-1||dis[j]<dis[u])
u=j;// 不提前退出的原因是还要作两点间最大值
}
}
tal+=dis[u];
vis[u]=1;
for(int j=1; j<=n; ++j)
{
if(vis[j]&&j!=u)//求从u到j的路径上最大边的权值=max(j到u的父节点,u-p)
{
path[u][j]=path[j][u]=max(path[j][pre[u]], dis[u]);
}
if(!vis[j] && dis[j]>mp[u][j])
{
dis[j]=mp[u][j];
pre[j]=u;
}
}
}
return tal;
}
signed main()
{
int t;
scanf("%lld",&t);
while(t--)
{
scanf("%lld",&n);
memset(mp,0,sizeof(mp));
for(int i=1; i<=n; ++i)
{
scanf("%lld%lld%lld",&p[i].x,&p[i].y,&wi[i]);
}
for(int i=1; i<=n; ++i)
{
for(int j=1; j<=n; ++j)
{
if(i!=j)
{
mp[i][j]=Dist(p[i],p[j]);
}
}
}
double tal=Prim();
double res=-1;
for(int i=1; i<=n; ++i)
{
for(int j=1; j<=n; ++j)
{
if(i!=j)
{
res=max(res,(wi[i]+wi[j])/(tal-path[i][j]));
}
}
}
printf("%.2f\n",res);
}
return 0;
}
既然已经写到这里了,那我再写个 \(kruskal\) 的次小生成树吧,用 \(lca\) 求出最大边,时间复杂度 \(O(m*logm + m*logn)\)。代码以后在说,怎么可能考到这种把时间卡死的偏门玩意。
code
// 好难,之后有兴趣再写

浙公网安备 33010602011771号