图论(最短路专题): P1552 牛的旅行 题解
01.题目理解
给你\(n(n<150)\)个点 \(m\)条无向带权边构成的多个联通块 问加一条边后连通块内直径的最小值
直径:连通块内距离最远2点的最短路
02.思路分析
step1:标记连通块
考虑染色法
题目中有多个连通块 我们可以从每个点开始跑一遍\(dfs\)
将搜索中经过的所有点和出发点染成相同颜色
即将连通块内所有的点染成相同颜色
void dfs(int u,int c)
{
col[u]=c;
for(edge ed:e[u])
{
int v=ed.v;
if(col[v]) continue;
dfs(v,c);
}
}
step2:找到连通块中任意2点的距离并计算以每个点为端点形成直径长度
这是一步预处理操作 避免第三步时间达到\(O(n^4)\)量级
我们发现\(n\)最大只有\(150\)
显然可以直接打\(O(n^3)floyd\)寻找每两个点间的距离
当然也可以打\(dijkstra\) 复杂度\(O(n^2\) \(log\) \(n)\)
//O(n^2 log n)
for(int i=1;i<=n;++i)
{
dijkstra(i);
for(int j=1;j<=n;++j)
if(inf-dis[j]>0.2)
max_dis[i]=max(dis[j],max_dis[i]);
}
step3:暴力连边 计算最小距离
现在我们有了每个点的\(max\)_\(dis\)值
显然对于我们可以对于两个不在同一连通块的点进行连边
连边后长度为\(max\)_ \(dis[i]+max\)_ \(dis[j]+len(i,j)\)
计算该值的最小值即可
坑点:直径在连边后可能仍然为最长边
原算法中将其误认为2连通块相连后的长度
所以要将\(ans\)和原来直径作比较 修改后即可\(AC\)
03.代码实现
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=200,inf=0x3f3f3f3f3f3f3f3f;
int x[N],y[N],col[N];
double max_dis[N];
struct edge
{
int v;
double w;
};
vector<edge> e[N];
struct node
{
double dis;
int u;
bool operator>(const node& a)const
{
return dis>a.dis;
}
};
double dis[N];
int vis[N];
priority_queue<node,vector<node>,greater<node> >q;
void dijkstra(int s)
{
for(int i=0;i<200;++i)
dis[i]=inf;
for(int i=0;i<200;++i)
vis[i]=0;
dis[s]=0;
q.push({0,s});
while(!q.empty())
{
int u=q.top().u;
q.pop();
if(vis[u])continue;
vis[u]=1;
for(auto ed:e[u])
{
double w=ed.w;
int v=ed.v;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
q.push({dis[v],v});
}
}
}
}
vector<int> farm[N];
double len(int a,int b)
{
int delx=x[a]-x[b];
int dely=y[a]-y[b];
return sqrt(delx*delx+dely*dely);
}
void dfs(int u,int c)
{
col[u]=c;
for(edge ed:e[u])
{
int v=ed.v;
if(col[v]) continue;
dfs(v,c);
}
}
signed main()
{
// freopen("x.in","r",stdin);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int n;
cin>>n;
for(int i=1;i<=n;++i)
cin>>x[i]>>y[i];
for(int i=1;i<=n;++i)
{
string str;
cin>>str;
str="0"+str;
for(int j=1;j<str.size();++j)
if(str[j]=='1')
e[i].push_back({j,len(i,j)});//存边
}
//todo1:寻找连通块
int c=1;
for(int i=1;i<=n;++i)
if(col[i]==0)
dfs(i,c),++c;
for(int i=1;i<=n;++i)
farm[col[i]].push_back(i);
//todo2:计算每个点的最远距离点
for(int i=1;i<=n;++i)
{
dijkstra(i);
for(int j=1;j<=n;++j)
if(inf-dis[j]>0.2)
max_dis[i]=max(dis[j],max_dis[i]);
}
//todo3:暴力连边 计算最小距离
double ans=LONG_LONG_MAX,ret=-1;
vector<int> i2;
for(int i=1;i<c;++i)
for(int j=i+1;j<c;++j)
for(int ik:farm[i])
for(int jk:farm[j])
{
if(ans>max_dis[ik]+max_dis[jk]+len(ik,jk))
i2.push_back(i),i2.push_back(j),ans=max_dis[ik]+max_dis[jk]+len(ik,jk);
}
for(int i:i2)
for(int ik:farm[i])
ans=max(ans,max_dis[ik]);
printf("%.6lf",ans);
}

浙公网安备 33010602011771号