Title

Floyd——人人都是中间商(50%)

Floyd——人人都是中间商

引入

  • 有n座相互孤立的岛屿,每座岛屿都拥有被其他岛屿搭建桥梁的机会,但在起始的时候,每座岛屿并没有向其他岛屿搭建桥梁的能力,突然有一天,资金突然丰富起来了,但只能按顺序分配给这些岛屿,并使得这些岛屿具有向其他岛屿搭建桥梁的能力,同时在搭建得过程中一旦发现以该座岛屿为中转站所连接的任意两座其他的岛屿之间的桥梁长度比原本的桥梁长度来得短,就拆掉原本的桥梁,搭起新桥梁,否则就放弃新的搭建计划。

粗糙的原理分析

  • Floyd本质上是动态规划,在未做滚动数组去优化来省去一个维度时,dist[k][i][j]表示的是在使用前k个结点时,点i到点j的最短距离,而当k==总结点时即已经纳入了所有的点(所有的情况)进行了考虑。在做了优化后可变为二维得dist[i][j]

注意点

初始化

  • 起初各自点并没有来当中介的点,只能自己到达自己 (给距离赋值为0),而无法到达别人家(给距离赋值为INF)

赋值

  • 可能出现重边的现象(所谓重边就是两个点之间存在两条及以上的边),这个时候我们应该保留较小的边就行。

适用

  1. 小空间稠密阵
  2. 能接受$ O(n^3)$的题目。

应用

模板题

AcWing 854. Floyd求最短路

#include<bits/stdc++.h>
using namespace std;
const int N= 2e2+10,INF =0x3f3f3f3f;	
int n,m,q;
int dist[N][N];

void floyd()
{
	for(int k=1;k<=n;k++)
	    for(int i=1;i<=n;i++)
	        for(int j=1;j<=n;j++)
	            dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);
}
int main()
{
    memset(dist,0x3f,sizeof(dist));
 
	cin>>n>>m>>q;
	for(int i=1;i<=n;i++)dist[i][i]=0;//起初各自点并没有中介的点,只能自己到达自己 
	for(int i=0;i<m;i++)
	{
		int x,y,z;
		cin>>x>>y>>z;
	    dist[x][y]=min(dist[x][y],z);//有可能出现重边 
	}
	
	floyd();
	
	for(int i=0;i<q;i++)
	{
		int x,y;
		cin>>x>>y;
		if(dist[x][y]!=INF)
		   cout<<dist[x][y]<<endl;
		else cout<<"impossible"<<endl;
	}
	return 0;
}

直径的拼凑问题

  • 我们希望配凑出的直径要尽可能小,找出来的点能够直接拼起来,而不是多加上几条边。
  • 要确定点,可以先用floyd对所有点先跑一遍。
  • double数组不要用0x3f填充,会得到一个比1还小的数
  • AcWing 1125. 牛的旅行,dededebug
#include<bits/stdc++.h>
using namespace std;
const int N = 260,INF = 99999999; 

int n;

struct dot{
	double x,y;
};

char g[N][N];//记录的仅仅是第i个点是否和第j点联通的情况 
double dist[N][N];

void floyd()
{
	for(int k=1;k<=n;k++)
	    for(int i=1;i<=n;i++)
	        for(int j=1;j<=n;j++)
	        	 dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);          
}

double get_dist(dot a,dot b)
{
	return sqrt( (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) );
}

int main()
{
	cin>>n;	

	for(int i=1;i<=n;i++) dist[i][i]=0;//错误点:不要让后面的操作覆盖掉它 
	dot dots[n+1];
	for(int i=1;i<=n;i++)
	   cin>>dots[i].x>>dots[i].y;
    //读入联通的情况 
	for(int i=1;i<=n;i++)
	    for(int j=1;j<=n;j++)
	    {
	    	cin>>g[i][j];
	    	if(g[i][j]=='1')
	    		dist[i][j]=get_dist(dots[i],dots[j]);
	    	else if(i!=j) 
	    	    dist[i][j]=INF;
		}

    floyd(); 
    //(间接联通)可以借用dist数组来判断联通关系比较模糊的两点之间的情况
	//不用再去写并查集
	//要找直径,就是要确立当前点中到所有连接的点中的最远距离 
	double ans1=0;//答案有可能在自己圈里,新加入的牧场由于长度太小,可能没有对直径产生影响 
	double max_dot_dis[N+1];
	memset(max_dot_dis,0,sizeof(max_dot_dis));

	for(int i=1;i<=n;i++)
	{
	    for(int j=1;j<=n;j++)
	    {
	        if(dist[i][j]!=INF)
	        {
	        	 max_dot_dis[i]=max( max_dot_dis[i], dist[i][j] );  //	cout<<max_dot_dis[i]<<" "; 
				 ans1=max( ans1, max_dot_dis[i] );   
			}
	          
		}
    }
    
	double ans2=INF;//比小要设大,比大要设小 
	for(int i=1;i<=n;i++)
	    for(int j=1;j<=n;j++)
	    	if(dist[i][j]==INF)//如果两者距离比较大(没有联通而已,但还是有距离),则认为两者分属不同的牧场 
			    ans2=min( max_dot_dis[i] + max_dot_dis[j] + get_dist(dots[i],dots[j]) ,ans2);//错误点2:取小 
    cout<<fixed<<setprecision(6)<<max(ans1,ans2);//这里是被迫取其大,两小中取较大者 
	return 0;
}

无向图的最小环问题

传递闭包

posted @ 2021-07-17 19:57  BeautifulWater  阅读(47)  评论(0编辑  收藏  举报