Floyd算法及其扩展应用

 Floyd算法的证明

void floyd()//三层循环实现floyd算法
{
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
         for(int j=1;j<=n;j++)
           d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}


 Floyd的运用

  1.牛的旅行(最短路

牛的旅行http://ybt.ssoier.cn:8088/problem_show.php?pid=1343 

#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
const int N=160;
const double INF=1e20;
typedef pair<int,int> pii;
pii p[N];
char g[N][N];
double dist[N][N];
double maxd[N];
int n;
double get_dist(pii a,pii 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=0;i<n;i++) cin>>p[i].x>>p[i].y;
  for(int i=0;i<n;i++) cin>>g[i];
  for(int i=0;i<n;i++)
     for(int j=0;j<n;j++)
        if(i!=j)
        {
            if(g[i][j]=='1') dist[i][j]=get_dist(p[i],p[j]);//假如是相连的可以直接算两点间的距离
            else dist[i][j]=INF;//反之为正无穷
        }
   for(int k=0;k<n;k++)//用Floyd更新两两的最短距离
      for(int i=0;i<n;i++)
         for(int j=0;j<n;j++)
            dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);
  for(int i=0;i<n;i++)//更新距离i这个点的最长距离
    for(int j=0;j<n;j++)
      if(dist[i][j]<INF)//假如在同个牧场才更新
          maxd[i]=max(maxd[i],dist[i][j]);
   double res1=0;
   for(int i=0;i<n;i++) res1=max(res1,maxd[i]);//求一遍每个牧场的最长直径,因为结果肯定大于两个牧场的直径
   double res2=INF;
   for(int i=0;i<n;i++)//枚举两个点相连
      for(int j=0;j<n;j++)
         if(dist[i][j]>=INF)//假如不在一个牧场才相连
            res2=min(res2,get_dist(p[i],p[j])+maxd[i]+maxd[j]);//假如这两个点相连
   printf("%lf\n",max(res1,res2));//输出二者的最大
    return 0;
}

2.排序(传递闭包)

343. 排序 - AcWing题库https://www.acwing.com/problem/content/345/

#include<bits/stdc++.h>
using namespace std;
const int N=30;
int d[N][N];
bool st[N];
int n,m;

void floyd()//用Floyd更新其他的点
{
    for(int k=0;k<n;k++)
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                d[i][j] |= d[i][k]&&d[k][j];
}
int check()
{
    for(int i=0;i<n;i++)
        if(d[i][i])   //假如自己小于自己则有矛盾
            return 2;
    for(int i=0;i<n;i++)
        for(int j=0;j<i;j++)
            if(!d[i][j] && !d[j][i])//假如i和j的关系还没确定
                return 0;
    
    return 1;
    
}
char get_min()//找最小的数
{
    for(int i=0;i<n;i++)
    {
        if(!st[i])
        {
            bool flag=true;
            for(int j=0;j<n;j++)
            {
                if(!st[j]&&d[j][i])//假如大于其中一个数了,说明这个数不是小于所有数
                {
                    flag=false;
                    break;
                }
            }
            if(flag)//假如这个数是小于所有数了,则返回
            {
                st[i]=true;
                return i+'A';
            }
        }
    }
}
int main()
{
    while(cin>>n>>m,n||m)
    {
        memset(d,0,sizeof d);
        int type=0,t;
        for(int i=1;i<=m;i++)
        {
            char str[5];
            cin>>str;
            int a=str[0]-'A',b=str[2]-'A';
            
            if(!type)
            {
                d[a][b]=1;//标记a<b
                floyd();//用floyd更新一遍
                type=check();//判断一下这一步
                if(type) t=i;//假如可以确定,则记录这一步

            }
        }
        if(!type)cout<<"Sorted sequence cannot be determined."<<endl;
        else if(type==2)cout<<"Inconsistency found after "<<t<<" relations."<<endl;
        else
        {
            memset(st,0,sizeof st);
            cout<<"Sorted sequence determined after "<<t<<" relations: ";
            for(int i=0;i<n;i++)
            cout<<get_min();
            cout<<"."<<endl;
        }
    }
}

3.观光之旅(找最小环)

344. 观光之旅 - AcWing题库https://www.acwing.com/problem/content/346/

#include <iostream>
#include <cstring>
#include <string>
#include <vector>

using namespace std ;

const int N = 110 , M = 10010 , INF = 0x3f3f3f3f ;


typedef long long LL ;
int g[N][N] , d[N][N] ;
int pos[N][N] ; //记录当前状态由哪个点转移过来
vector<int> path ;
int n , m ;

//确保顺序正确
void dfs(int i , int j )    //i->j之间的路,输出i到j之间不包括i和j的道路
{
    int k = pos[i][j] ;

    if( k == 0 )    return ;    //如果是0,说明i,j之间不经过除i,j之外的其他点

    dfs(i , k); //i->newk
    path.push_back(k);  //k
    dfs(k , j); //newk->j

}

void get_path(int i , int j , int k )
{
    path.clear() ;
    path.push_back(k);  //边界
    path.push_back(i);
    dfs(i , j) ;    //k->i->j->k
    path.push_back(j);
}



int main()
{
    cin >> n >> m ; 

    memset(g , 0x3f ,sizeof g) ;
    for(int i = 0 ; i <= n ; i++ )  g[i][i] = 0 ;

    int a , b , c ;
    for(int i = 0 ; i < m ; i++ )
    {
        cin >> a >> b >> c ;
        g[a][b] = g[b][a] = min(g[a][b] , c) ;
    }

    memcpy(d , g , sizeof d );  //原图
    long long res = INF ;

    for(int k = 1 ; k <= n ; k++ )
    {
        //至少包含三个点的环所经过的点的最大编号是k
        for(int i = 1 ; i < k ; i++ )  //至少包含三个点,i,j,k不重合
            for(int j = i + 1 ; j < k ; j ++ )
            if(res > (LL)g[i][j] + d[i][k] + d[k][j] )
            {
                res = g[i][j] + d[i][k] + d[k][j] ;
                get_path(i , j , k) ;
            }

        for(int i = 1 ; i <= n ; i++ )
            for(int j = 1 ; j <= n ; j++ )
                if(g[i][j] > g[i][k] + g[k][j])
                {
                    g[i][j] = g[i][k] + g[k][j] ;
                    pos[i][j] = k ; 
                }
    }

    if(res == INF)  
        cout << "No solution." << endl;
    else
    {
        for(auto x : path)
            cout << x << ' ' ;
        cout << endl;
    }

    return 0;
}

4.牛站(快速幂求n次方+用map离散化)

345. 牛站 - AcWing题库https://www.acwing.com/problem/content/347/

离散数学的图论中经过k条边就是恰好矩阵的k次方的最小值

#include<bits/stdc++.h>
using namespace std;
const int N=210;
int k,n,m,S,E;
unordered_map<int,int> id;
int g[N][N],res[N][N];
void mul(int c[][N],int a[][N],int b[][N])//状态计算
{
    static int temp[N][N];
    memset(temp,0x3f,sizeof temp);
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
           for(int j=1;j<=n;j++)
              temp[i][j]=min(temp[i][j],a[i][k]+b[k][j]);//假如a中的i~k+b的k~j小于我i~j则更新
    memcpy(c,temp,sizeof temp);//把答案传回去
}
void qmi()
{
    memset(res,0x3f,sizeof res);
    for(int i=1;i<=n;i++) res[i][i]=0;//自己到自己也合法
    while(k)
    {
        if(k&1) mul(res,res,g);//res=res*g
        mul(g,g,g);//g=g*g
        k>>=1;
    }
}
int main()
{
    memset(g,0x3f,sizeof g);
    cin>>k>>m>>S>>E;
    if(!id.count(S)) id[S]=++n;//假如S还没离散化,就离散化
    if(!id.count(E)) id[E]=++n;//假如E还没离散化,就离散化
    S=id[S],E=id[E];//等于离散化后的值
    while(m--)
    {
        int a,b,c;
        cin>>c>>a>>b;
        if(!id.count(a)) id[a]=++n;//假如a还没离散化,就离散化
        if(!id.count(b)) id[b]=++n;//假如b还没离散化,就离散化
        a=id[a],b=id[b];//等于离散化后的值
        g[a][b]=g[b][a]=min(g[a][b],c);
    }
    qmi();//快速幂
    cout<<res[S][E]<<endl;
    return 0;
}

扫描二维码关注公众号,回复: 16249771 查看本文章

猜你喜欢

转载自blog.csdn.net/m0_64378422/article/details/131037693