sakuya726's 图论大一统计划1(最短路)
基础知识
图的存储方式:
①:邻接矩阵。空间复杂度为O(n2)。
②:邻接表。空间复杂度为O(n+m)。m为边集的大小。
邻接表的存图和遍历:
struct node { int v,nxt,val; }s[maxn]; int head[maxn],tot; inline void add(int x,int y,int z) { s[++tot].v=y; s[tot].val=z; s[tot].nxt=head[x]; head[x]=tot; } inline void dfs(int now) { for(rg int i=head[now];i;i=s[i].nxt) { int v=s[i].v; dfs(v); } }
图的基本类型:
①:有向图。点与点之间通过有方向的边进行连接。
②:无向图。点与点之间通过不带有方向的边进行连接,也可以理解成两个点之间连接着两条指向对方的单向边。
单元最短路径
定义:给定一张有向图G和起点S,需要我们求出从S出发,到其余各点的最短距离。
Dijkstra算法
流程如下:
1.初始化dis数组。dis[st]=0,其余的dis值置为无穷大。(起点到它自身的距离当然是0)。
2.找出一个未被标记的、dis[x]最小的节点x,然后标记该节点。
3.扫描节点x的所有出边(x,y,z),如果dis[y]>dis[x]+z,dis[y]=dis[x]+z。即当前已知的从起点st到目标节点y的最短路dis[y]要大于从st到x再到y的长度dis[x]+z,那么我们就更新从st到y的最短路dis[y]。
4.重复上述过程,直到标记完所有节点。
原理阐述:
Dijkstra基于贪心的思想,不适用于带有负边权的图。当边长都是非负数的时候,全局最小值不能再被其他节点更新,因此在第1步中选出的节点x必然满足:dis[x]已经是从st到x的最短路径。我们不断选择全局最小的dis进行标记和更新拓展。最终得到的st到其他节点的长度必然是最短路。
优化方法:
上述阐述的过程中,比较消耗时间的是找到尚未被标记的dis[x]最小的节点x和从该节点发展其子节点进行拓展。对于后者我们无法优化,因为对于必要的节点我们如果不拓展发展也就无法求出最优路径。但是对于前者我们可以进行优化,既然要找到全局最小的尚未被标记的dis[x],我们可以使用一个小根堆来进行维护,这样就把O(n)的寻找优化到了O(logn)。算法的整体复杂度也就降低到了O(mlogn)。
#include<bits/stdc++.h> using namespace std; #define rg register #define INF 1e10 #define maxn 204010 inline int read() { int x=0,f=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') { f=-1; } c=getchar(); } while(c<='9'&&c>='0') { x=(x<<1)+(x<<3)+(c^48); c=getchar(); } return x*f; } template <typename qaq >inline void print(qaq x) { if(x<0) { x=-x; putchar('-'); } if(x>=10) { print(x/10); } putchar(x%10+'0'); } bool flag[maxn]; int dis[maxn]; int n,m,st; struct node { int v,nxt,val; }s[maxn]; int head[maxn],tot; inline void add(int x,int y,int z) { s[++tot].v=y; s[tot].val=z; s[tot].nxt=head[x]; head[x]=tot; } inline void pre_work() { for(rg int i=1;i<=n;++i) dis[i]=INF; dis[st]=0; } priority_queue< pair<int,int> >q; inline void Dijkstra() { q.push(make_pair(0,st)); while(!q.empty()) { rg int top=q.top().second; q.pop(); if(flag[top]) continue; flag[top]=1; for(rg int i=head[top];i;i=s[i].nxt) { rg int v=s[i].v; rg int w=s[i].val; if(dis[v]>dis[top]+w) { dis[v]=dis[top]+w; q.push(make_pair(-dis[v],v)); } } } } int main() { scanf("%d%d%d",&n,&m,&st); for(rg int i=1;i<=m;++i) { int x,y,z; x=read(); y=read(); z=read(); add(x,y,z); } pre_work(); Dijkstra(); for(rg int i=1;i<=n;++i) printf("%d ",dis[i]); return 0; }
SPFA算法
原理阐述:
对于一张图G中的某一条边(x,y,z),若是满足dis[y]<=dis[x]+z,就称该边满足三角形不等式。若是所有边都满足了该不等式,则得到的dis数组就是最短路。
流程如下:
1.建立一个队列,最初队列中只有起点1。
2.取出队列中的头节点x,遍历它的所有出边(x,y,z),若是不满足三角形不等式,则更新dis[y]=dis[x]+z。同时如果y不在队列中,则将y入队。
3.重复上述操作,直到队列为空。
inline void spfa() { memset(dis,inf,sizeof(dis)); memset(vis,0,sizeof(vis)); dis[st]=0; vis[st]=1; queue<int>q; q.push(st); while(q.size()) { int top=q.front(); q.pop(); vis[top]=0; for(rg int i=head[top];i;i=s[i].nxt) { int v=s[i].v; if(dis[v]>dis[top]+s[i].val) { dis[v]=dis[top]+s[i].val; if(vis[v]==0) { q.push(v); vis[v]=1; } } } } }
Dijkstra和SPFA的区别
1.SPFA算法可以处理带负权的边,而Dijkstra算法不行。
2.Dijkstra算法采用贪心策略,每次选择距当前最近的点进行延伸(贪心,不公平),Dijkstra算法采用贪心策略,每次选择距当前最近的点进行延伸(贪心,不公平)。
两者相结合的最短路算法
将堆优化Dijkstra中的记录判断访问的部分删除即可得到。
如果图中可能有负值圈,就再把SPFA检测负值圈的部分加进来。
检测原理如下:
负值圈存在时会有无限次入队的节点,有无限次被松弛的边。无负值圈时,每个节点入队次数不会超过n次,最长的最短路径也不会拥有超过n-1条边。
刷题时间
Telephone Lines(POJ 3662)
思路:二分答案+最短路的思想。二分答案我们所求的上限,然后通过类似BFS的方式来不断更新每个节点的最优值,这里的话有点类似SPFA的感觉。
#include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <cmath> #include <ctime> using namespace std; #define rg register #define INF 1e10 #define maxn 204010 inline int read() { int x=0,f=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') { f=-1; } c=getchar(); } while(c<='9'&&c>='0') { x=(x<<1)+(x<<3)+(c^48); c=getchar(); } return x*f; } template <typename qaq >inline void print(qaq x) { if(x<0) { x=-x; putchar('-'); } if(x>=10) { print(x/10); } putchar(x%10+'0'); } int n,m,k; struct node { int v,nxt,val; }s[maxn]; int head[maxn],tot; inline void add(int x,int y,int z) { s[++tot].v=y; s[tot].val=z; s[tot].nxt=head[x]; head[x]=tot; } int dis[maxn],num[maxn]; inline int bfs(int top) { memset(num,0x3f,sizeof(num)); queue<int>q; q.push(1); num[1]=0; while(q.size()) { int now=q.front(); q.pop(); for(rg int i=head[now];i;i=s[i].nxt) { int v=s[i].v; if(s[i].val>top) { if(num[v]>num[now]+1) { num[v]=num[now]+1; q.push(v); } } else { if(num[v]>num[now]) { num[v]=num[now]; q.push(v); } } } } if(num[n]==0x3f3f3f3f) { cout<<-1; exit(0); } return num[n]; } inline bool check(int top) { if(bfs(top)>k) return false; return true; } int main() { cin>>n>>m>>k; for(rg int i=1;i<=m;++i) { int a,b,c; a=read(); b=read(); c=read(); add(a,b,c); add(b,a,c); } int l=0,r=1000005,ans=1; while(l<=r) { int mid=(l+r)>>1; if(check(mid)==1) { ans=mid; r=mid-1; } else l=mid+1; } cout<<ans; }
最优贸易(LG P1073)
思路:思考一下本题所求的最优解的过程。①从1号点出发找到一个售价极低的点购买。②从这个点出发到达一个售价极高的点出售。③再从这个点抵达n号点。
因此我们就可以得到想要解本题的关键步骤:找到从1号出发能够抵达的售价极低的点;找到能够抵达n号点的售价极高的点。为了完成这两步,很明显我们需要以1号点和n号点为起点跑两次bfs,其中前者是为了找到极小值,且从1号出发是正常建图,后者我们虽然是抵达n号点,但是也可以理解为从n号点出发跑一遍反向图。当我们完成这两遍bfs时候,对于每个点的极大极小值就已经确定了,最后只需要O(n)扫一遍就能得到答案。
#include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <cmath> #include <ctime> using namespace std; #define rg register #define INF 1e10 #define maxn 204010 inline int read() { int x=0,f=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') { f=-1; } c=getchar(); } while(c<='9'&&c>='0') { x=(x<<1)+(x<<3)+(c^48); c=getchar(); } return x*f; } template <typename qaq >inline void print(qaq x) { if(x<0) { x=-x; putchar('-'); } if(x>=10) { print(x/10); } putchar(x%10+'0'); } int n,m; int pri[maxn]; struct node { int v,nxt; }s[maxn][3]; int head[maxn][3],tot; inline void add1(int x,int y) { s[++tot][0].v=y; s[tot][0].nxt=head[x][0]; head[x][0]=tot; } inline void add2(int x,int y) { s[tot][1].v=x; s[tot][1].nxt=head[y][1]; head[y][1]=tot; } bool vis[maxn]; int Fmin[maxn]; int Fmax[maxn]; int ans; inline void dj(int st,int f[],bool flag) { memset(vis,0,sizeof(vis)); vis[st]=1; queue<int>q; q.push(st); if(flag==0) { for(rg int i=1;i<=n;++i) f[i]=INF; } else { for(rg int i=1;i<=n;++i) f[i]=0; } f[st]=pri[st]; while(q.size()) { int now=q.front(); q.pop(); for(rg int i=head[now][flag];i;i=s[i][flag].nxt) { int v=s[i][flag].v; if(flag==0) f[v]=min(f[v],min(f[now],pri[v])); else f[v]=max(f[v],max(f[now],pri[v])); if(vis[v]==0) { vis[v]=1; q.push(v); } } } } int main() { cin>>n>>m; for(rg int i=1;i<=n;++i) pri[i]=read(); for(rg int i=1;i<=m;++i) { int a,b,c; a=read(); b=read(); c=read(); add1(a,b); add2(a,b); if(c==2) { add1(b,a); add2(b,a); } } dj(1,Fmin,0); dj(n,Fmax,1); if(Fmax[n]==0||Fmin[n]==INF) cout<<0; else { ans=0; for(rg int i=1;i<=n;++i) ans=max(ans,Fmax[i]-Fmin[i]); cout<<ans; } }
道路与航线(BZOJ 2200)
思路:本身是一个单源最短路径问题,但是因为数据恶意卡SPFA,加上DJ没法处理负边权。所以实际上做起来远比我想象中的要麻烦。注意到图中所说的条件,负边只是单向边,并且如果存在从A到B的航线,那么就无法从B再到A。翻译一下就是说不存在有向边的环。因此,我们可以把无向边连接的点缩成一个联通块,每个联通块之间就由有向边连接。这样就成了一个经典的DAG。在联通块内部因为不存在负边,因此我们可以用DJ来跑,而联通块之间则就用拓扑排序处理就行。
#include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <cmath> #include <ctime> using namespace std; #define rg register #define INF 1e10 #define maxn 204010 inline int read() { int x=0,f=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') { f=-1; } c=getchar(); } while(c<='9'&&c>='0') { x=(x<<1)+(x<<3)+(c^48); c=getchar(); } return x*f; } template <typename qaq >inline void print(qaq x) { if(x<0) { x=-x; putchar('-'); } if(x>=10) { print(x/10); } putchar(x%10+'0'); } struct node { int v,nxt,val; }s[maxn]; int head[maxn],tot; inline void add(int x,int y,int z) { s[++tot].v=y; s[tot].val=z; s[tot].nxt=head[x]; head[x]=tot; } int t,r,p,st; int part[maxn],cnt;//连通块以及编号 vector<int> tag[maxn]; //存储每个连通块中的点 inline void dfs(int x) { if(part[x]!=0) return ; part[x]=cnt; tag[cnt].push_back(x); for(rg int i=head[x];i;i=s[i].nxt) dfs(s[i].v); } int dg[maxn];//统计入度 用于之后的topsort int dis[maxn]; queue<int>top_queue; priority_queue<pair<int,int> >q; bool vis[maxn]; inline void dj(int top) { for(rg int i=0;i<tag[top].size();++i) q.push(make_pair(-dis[tag[top][i]],tag[top][i])); while(q.size()) { int now=q.top().second; q.pop(); if(vis[now]==1) continue; vis[now]=1; for(rg int i=head[now];i;i=s[i].nxt) { int v=s[i].v; if(dis[v]>dis[now]+s[i].val) dis[v]=dis[now]+s[i].val; if(part[v]!=top) { dg[part[v]]--; if(dg[part[v]]==0) top_queue.push(part[v]); } else q.push(make_pair(-dis[v],v)); } } } inline void topsort() { for(rg int i=1;i<=cnt;++i) { if(dg[i]==0) top_queue.push(i); } while(top_queue.size()) { int now=top_queue.front(); top_queue.pop(); dj(now); } } int main() { //fc road1.out ans.out cin>>t>>r>>p>>st; for(rg int i=1;i<=r;++i) { int a,b,c; a=read(); b=read(); c=read(); add(a,b,c); add(b,a,c); } for(rg int i=1;i<=t;++i)//O(n)跑一次dfs建立连通块 { if(part[i]==0) ++cnt,dfs(i); } for(rg int i=1;i<=p;++i) { int a,b,c; a=read(); b=read(); c=read(); dg[part[b]]++; add(a,b,c); } memset(dis,0x3f,sizeof(dis)); dis[st]=0; topsort(); for(rg int i=1;i<=t;++i) { if(dis[i]>=1e8) cout<<"NO PATH"<<endl; else cout<<dis[i]<<endl; } }
Geodetic集合(LG P3906)
思路:Floyd水题,提前预处理出任意两个点的最短路径长度,对于集合中的点x,必定满足f[a][x]+f[x][b]=f[a][b]。由这个式子就可以得到答案了。
#include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <cmath> #include <ctime> using namespace std; #define rg register #define inf 1e9+5 #define maxn 300 inline int read() { int x=0,f=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') { f=-1; } c=getchar(); } while(c<='9'&&c>='0') { x=(x<<1)+(x<<3)+(c^48); c=getchar(); } return x*f; } template <typename qaq >inline void print(qaq x) { if(x<0) { x=-x; putchar('-'); } if(x>=10) { print(x/10); } putchar(x%10+'0'); } int n,m,k; int f[maxn][maxn]; inline void floyd() { for(rg int k=1;k<=n;++k) { for(rg int i=1;i<=n;++i) { if(f[i][k]==inf) continue; for(rg int j=1;j<=n;++j) { if(f[k][j]==inf) continue; f[i][j]=min(f[i][j],f[i][k]+f[k][j]); } } } } int a,b; inline void find(int x,int y) { for(rg int i=1;i<=n;++i) { if(f[x][i]+f[i][y]==f[x][y]) { cout<<i<<" "; } } cout<<endl; } int main() { n=read(); m=read(); for(rg int i=1;i<=n;++i) { for(rg int j=1;j<=n;++j) { if(i==j) f[i][j]=0; else f[i][j]=inf; } } for(rg int i=1;i<=m;++i) { a=read(); b=read(); f[a][b]=f[b][a]=1; } floyd(); k=read(); for(rg int i=1;i<=k;++i) { a=read(); b=read(); find(a,b); } }
调手表(LG P8674)
思路:第一眼看上去好像和图论没有关系。但是实际上认真想想我们可以把题意抽象为图论相关的题。建立一个从0到n-1的点的有向图。每个点分别向它的i+1和i+k号点连一条边(对于超出n的需要进行mod处理)。最后我们统计一遍从0出发到其他各点的最短路的最大值是多少就可以了。
#include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <cmath> #include <ctime> using namespace std; #define rg register #define inf 1e9+5 #define maxn 233300 inline int read() { int x=0,f=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') { f=-1; } c=getchar(); } while(c<='9'&&c>='0') { x=(x<<1)+(x<<3)+(c^48); c=getchar(); } return x*f; } template <typename qaq >inline void print(qaq x) { if(x<0) { x=-x; putchar('-'); } if(x>=10) { print(x/10); } putchar(x%10+'0'); } int n,k; struct node { int v,val,nxt; }s[maxn]; int head[maxn],tot; inline void add(int x,int y,int z) { s[++tot].v=y; s[tot].val=z; s[tot].nxt=head[x]; head[x]=tot; } int dis[maxn]; bool vis[maxn]; priority_queue<pair<int,int> >q; inline void dj() { vis[0]=1; q.push(make_pair(0,0)); while(q.size()) { int now=q.top().second; q.pop(); for(rg int i=head[now];i;i=s[i].nxt) { int v=s[i].v; if(dis[v]>dis[now]+s[i].val) dis[v]=dis[now]+s[i].val; if(vis[v]==0) { vis[v]=1; q.push(make_pair(-dis[v],v)); } } } } int maxx; int main() { n=read(); k=read(); k%=n; for(rg int i=0;i<n;++i) { int fir=(i+1)%n; int sed=(i+k)%n; add(i,fir,1); add(i,sed,1); } memset(dis,0x3f,sizeof(dis)); dis[0]=0; dj(); for(rg int i=0;i<n;++i) maxx=max(maxx,dis[i]); cout<<maxx; }
梦境(LG P8914)
思路:很恶心一道题,难点主要有俩。第一是输出格式问题,这里参考评论区大佬用cout.precision(15)解决了。第二点就是关于小A的路径选择,因为对于同样长度的最短路,他会优先选择经过点的编号所顺次构成序列的字典序最小的那条最短路。所以我一开始是以S为起点跑了一次单源最短路,然后再经历了长达三个小时的debug调试中终于意识到了一个问题。这里关于A的路径不能以S为起点来跑DJ,而是应该以终点f来反向跑最短路。原因粗略解释如下:因为DJ是用当前离起点最近的dis来不断更新尝试,那么如果我们假设一个单元最短路它的第一个节点满足字典序最小但是离起点非常远(即它的dis会很晚才能更新到),这样找到的路径就是错误的。而如果我们从f开始反向跑图就不会用这种错误。
#include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <cmath> #include <ctime> using namespace std; #define rg register #define inf 1e9+5 #define maxn 400300 inline int read() { int x=0,f=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') { f=-1; } c=getchar(); } while(c<='9'&&c>='0') { x=(x<<1)+(x<<3)+(c^48); c=getchar(); } return x*f; } template <typename qaq >inline void print(qaq x) { if(x<0) { x=-x; putchar('-'); } if(x>=10) { print(x/10); } putchar(x%10+'0'); } int n,m,f; struct node { int v,val,nxt; }s[maxn]; int head[maxn],tot; inline void add(int x,int y,int z) { s[++tot].v=y; s[tot].val=z; s[tot].nxt=head[x]; head[x]=tot; } int dis2[maxn]; int dis1[maxn]; int Path[maxn]; bool vis[maxn]; int S,B; priority_queue<pair<int,int> >q; inline void dj1() { memset(vis,0,sizeof(vis)); for(rg int i=0;i<=n;++i) dis1[i]=inf; vis[f]=1; dis1[f]=0; Path[f]=-1; q.push(make_pair(0,f)); while(q.size()) { int now=q.top().second; q.pop(); for(rg int i=head[now];i;i=s[i].nxt) { int v=s[i].v; if(dis1[v]>dis1[now]+s[i].val) { dis1[v]=dis1[now]+s[i].val; Path[v]=now; } else if(dis1[v]==dis1[now]+s[i].val) Path[v]=min(Path[v],now); if(vis[v]==0) { vis[v]=1; q.push(make_pair(-dis1[v],v)); } } } } inline void dj2() { memset(vis,0,sizeof(vis)); for(rg int i=0;i<=n;++i) dis2[i]=inf; vis[B]=1; dis2[B]=0; q.push(make_pair(0,B)); while(q.size()) { int now=q.top().second; q.pop(); for(rg int i=head[now];i;i=s[i].nxt) { int v=s[i].v; if(dis2[v]>dis2[now]+s[i].val) dis2[v]=dis2[now]+s[i].val; if(vis[v]==0) { vis[v]=1; q.push(make_pair(-dis2[v],v)); } } } } int a,b,c; double minnt=1e10; int main() { cout.precision(15); cin>>n>>m>>S>>B>>f; for(rg int i=1;i<=m;++i) { a=read(); b=read(); c=read(); add(a,b,c); add(b,a,c); } dj1(); dj2(); if(dis1[S]*3>=dis2[f]*2) { cout<<"NO"<<endl; for(rg int i=S;i!=-1;i=Path[i]) { double t=dis2[i]/3.0;//怪物到达这个点时候所用的时间 if(2.0*t<(double)(dis1[S]-dis1[i])) t+=double((dis1[S]-dis1[i])-2.0*t)/5.0;//两者相向而行 else if(fabs(2.0*t-(double)(dis1[S]-dis1[i]))<1e-9) minnt=min(minnt,t); else t+=double(2.0*t-(dis1[S]-dis1[i])); minnt=min(minnt,t); } cout<<minnt; } else { cout<<"YES"<<endl; if((dis1[S]*3)%2==0) cout<<dis2[f]-(dis1[S]*3)/2; else cout<<(double)dis2[f]-double(dis1[S]*3.0)/2.0; } }
Cow Tours(LG P1522)
思路:因为并查集用的不熟导致又前前后后改了两个多小时,题目本身不难,一眼出思路。floyd求出同一个联通块内的最短距离后枚举需要新加路径的两个端点就行了。最后的答案分三种情况考虑。①是原先A块中的直径。②是原先B块中的直径。③是通过新加路径的直径,即两个块中离路径两端点最远的距离+新加路径的距离。
#include<bits/stdc++.h> using namespace std; #define rg register #define maxn 400 #define inf 1e15 inline int read() { int x=0,f=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') f=-1; c=getchar(); } while(c<='9'&&c>='0') { x=(x<<1)+(x<<3)+c-48; c=getchar(); } return x*f; } struct node { int x,y; }s[maxn]; int n; int fa[maxn]; double f[maxn][maxn],ans=inf; inline int find(int x) { if(x==fa[x]) return x; else return fa[x]=find(fa[x]); } inline double get_dis(int x1,int y1,int x2,int y2) { return sqrt(double(pow(x1-x2,2))+pow(y1-y2,2)); } bool vis[maxn]; double pdis[maxn];//原来每个牧区的最短 vector<int>q[maxn]; vector<int>tp; inline void floyd() { for(rg int k=1;k<=n;++k) { for(rg int i=1;i<=n;++i) { for(rg int j=1;j<=n;++j) f[i][j]=min(f[i][j],f[i][k]+f[k][j]); } } for(rg int i=0;i<tp.size();++i) { pdis[tp[i]]=0; for(rg int j=0;j<q[tp[i]].size();++j) { for(rg int k=j+1;k<q[tp[i]].size();++k) { pdis[tp[i]]=max(pdis[tp[i]],f[q[tp[i]][j]][q[tp[i]][k]]); } } } } inline double get_mindis(int a,int b) { double minn=max(pdis[a],pdis[b]); double temp=inf; for(rg int i=0;i<q[a].size();++i) { for(rg int j=0;j<q[b].size();++j) { double dis1=0; double dis2=0; for(rg int k=0;k<q[a].size();++k) dis1=max(dis1,f[q[a][i]][q[a][k]]); for(rg int k=0;k<q[b].size();++k) dis2=max(dis2,f[q[b][j]][q[b][k]]); double sum=dis1+dis2+get_dis(s[q[a][i]].x,s[q[a][i]].y,s[q[b][j]].x,s[q[b][j]].y); temp=min(temp,sum); } } return max(temp,minn); } int main() { n=read(); for(rg int i=1;i<=n;++i) { s[i].x=read(); s[i].y=read(); fa[i]=i; } for(rg int i=1;i<=n;++i) { for(rg int j=1;j<=n;++j) { int c; scanf("%1d",&c); if(i==j) f[i][j]=0; else if(c==0) f[i][j]=f[j][i]=inf; else { f[i][j]=get_dis(s[i].x,s[i].y,s[j].x,s[j].y); int ifa=find(i); int jfa=find(j); if(ifa!=jfa) fa[jfa]=ifa; } } } for(rg int i=1;i<=n;++i) { fa[i]=find(fa[i]); q[fa[i]].push_back(i); if(!vis[fa[i]]) { vis[fa[i]]=1; tp.push_back(fa[i]); } } floyd(); for(rg int i=0;i<tp.size();++i)//枚举两个牧场 { for(rg int j=i+1;j<tp.size();++j) ans=min(ans,get_mindis(tp[i],tp[j])); } printf("%.6lf",ans); }

浙公网安备 33010602011771号