floyd 进阶

· · 个人记录

本课知识

主要学 floyd

floyd 求树的深度

  1. 直接跑 DFS ,得到递推式 dep[i] = dep[fa] + 1;
  2. floyd,dis[root(根节点)][i] 其实就是 dep[i]

floyd 求树的宽度

  1. BFS,找到入队节点个数最多的一层 (不好写)
  2. DFS,拿一个桶 T,用来装 dep[i](dep[i]相同的就是同一层的),找到最多的那个。
  3. floyd,与 DFS 一样,上文已经说过 dis[root(根节点)][i] 其实就是 dep[i]

floyd 在树上问题很好用,但是容易被卡空间时间(时间 O(n^3) ,空间 O(n^2) )

  1. 可以求深度
  2. 可以求宽度
  3. 可以求任意两点的距离

如用别的算法,代码较长(如 LCA )

P3884 [JLOI2009] 二叉树问题

数据范围 n 最多才 100 , floyd 轻松过

Code

LCA

自己额外写的

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
typedef long long ll;
const ll MX = 7;
ll n,root,dep_mx,t[105],mx,dep[105],dp[105][10];
bool vis[500005];
vector<ll> G[500005];

void dfs(ll x,ll f){
    dep[x] = dep[f] + 1;
    dp[x][0] = f;
    for(int i = 1;(1 << i) <= dep[x];i++){
        dp[x][i] = dp[dp[x][i - 1]][i - 1]; 
    }
    for(int i = 0;i < G[x].size();i++){
        ll nx = G[x][i];
        if(nx != f){
            dfs(nx,x);
        }
    }
    return ;
}

ll LCA(ll x,ll y){
    bool f = 0;
    ll lx = 0,ly = 0;
    if(dep[x] < dep[y]) swap(x,y),f = 1;
    for(int i = MX;i >= 0;i--){
        if(dep[x] - (1 << i) >= dep[y]){
            x = dp[x][i];
            lx += (1 << i);
        }
    }
    if(x == y){
        if(f) return lx + 2 * ly;
        else return 2 * lx + ly;
        return 0;
    }
    for(int i = MX;i >= 0;i--){
        if(dp[x][i] != dp[y][i]){
            x = dp[x][i];
            y = dp[y][i];
            lx += (1 << i);
            ly += (1 << i);
        }
    }
    lx++;
    ly++;
    if(f) return lx + 2 * ly;
    else return 2 * lx + ly;
    return 0;
} 

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n;
    for(int i = 1;i < n;i++){
        ll s,e;
        cin >> s >> e;
        G[s].push_back(e);
        G[e].push_back(s);
        vis[e] = 1;
    }
    for(int i = 1;i <= n;i++){
        if(vis[i] == 0){
            root = i;
        }
    }
    dfs(root,0);
    for(int i = 1;i <= n;i++){
        dep_mx = max(dep_mx,dep[i]);
        t[dep[i]]++;
        mx = max(mx,t[dep[i]]);
    }
    cout << dep_mx << '\n' << mx << "\n";
    ll x,y;
    cin >> x >> y;
    cout << LCA(x,y);
    return 0;
} 

floyd

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
typedef long long ll;
ll n,dep[105],root,dis[105][105],t[105],dep_mx,mx;

void init(){
    for(int i = 1;i <= n;i++){
        for(int j = 1;j <= n;j++){
            dis[i][j] = 1e14;
            if(i == j){
                dis[i][j] = 0;
            }
        }
    }
}

void floyd(){
    for(int x = 1;x <= n;x++){
        for(int s = 1;s <= n;s++){
            for(int e = 1;e <= n;e++){
                dis[s][e] = min(dis[s][e],dis[s][x] + dis[x][e]);
            }
        }
    }
    return ;
}

int main(){
    cin >> n;
    init();
    for(int i = 1;i < n;i++){
        ll s,e;
        cin >> s >> e;
        dis[s][e] = 1;
        dis[e][s] = 2;
    }
    floyd();
    for(int i = 1;i <= n;i++){
        dep[i] = dis[1][i];
        dep_mx = max(dep_mx,dep[i]);
        t[dep[i]]++;
        mx = max(mx,t[dep[i]]);
    }
    cout << dep_mx + 1 << "\n" << mx << "\n";
    ll s,e;
    cin >> s >> e;
    cout << dis[s][e] << "\n";
    return 0;
} 

P2888 [USACO07NOV] Cow Hurdles S

直接跑 floyd ,可以非常容易的得到递推式 ``a[s][e] = min(a[s][e],max(a[s][x],a[x][e]));`` ### Code ```cpp #include <iostream> #include <cstdio> #include <algorithm> #include <cmath> using namespace std; typedef long long ll; ll n,m,a[305][305]; void init(){ for(int i = 1;i <= n;i++){ for(int j = 1;j <= n;j++){ a[i][j] = 1e18; if(i == j) a[i][j] = 0; } } return ; } void floyd(){ for(int x = 1;x <= n;x++){ for(int s = 1;s <= n;s++){ for(int e = 1;e <= n;e++){ a[s][e] = min(a[s][e],max(a[s][x],a[x][e])); } } } return ; } void work(){ ll s,e; cin >> s >> e; if(a[s][e] < 1e17) cout << a[s][e] << "\n"; else cout << "-1\n"; return ; } int main(){ ll T; cin >> n >> m >> T; init(); while(m--){ ll s,e,c; cin >> s >> e >> c; a[s][e] = min(a[s][e],c); } floyd(); while(T--) work(); return 0; } ``` ## [P2912 [USACO08OCT] Pasture Walking G](https://www.luogu.com.cn/problem/P2912) 之前为了方便,直接三重,没有加上任何优化。 现在 $n$ 最大 1000 ,我在原来的代码上加上了一句剪枝优化: ``if(a[s][x] > 1e17) continue;`` 这一句话的意思是:如果没有 $x \to s$ 的路,就跳过。这一句话的优化效果在稀疏图中优化效果优异。 ### Code ```cpp #include <iostream> #include <cstdio> #include <algorithm> #include <cmath> using namespace std; typedef long long ll; ll n,m,a[1005][1005]; void init(){ for(int i = 1;i <= n;i++){ for(int j = 1;j <= n;j++){ a[i][j] = 1e18; if(i == j) a[i][j] = 0; } } } void floyd(){ for(int x = 1;x <= n;x++){ for(int s = 1;s <= n;s++){ if(a[s][x] > 1e17) continue; for(int e = 1;e <= n;e++){ a[s][e] = min(a[s][e],a[s][x] + a[x][e]); } } } return ; } int main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); ll T; cin >> n >> T; init(); for(int i = 1;i < n;i++){ ll s,e,c; cin >> s >> e >> c; a[s][e] = min(a[s][e],c); a[e][s] = min(a[e][s],c); } floyd(); while(T--){ ll s,e; cin >> s >> e; cout << a[s][e] << "\n"; } return 0; } ``` ## [P1841 [JSOI2007] 重要的城市](https://www.luogu.com.cn/problem/P1841) 这一题对于 floyd 的理解需要很深。 拿一个 ``ans`` 数组来存储答案, ``ans[s][e]`` 表示从 ``s`` 到 ``e`` 中间的关键城市。 若 ``ans[s][e]`` 为0,表示从 ``s`` 到 ``e`` 中间没有关键城市。 对于每一个中转点,我们可以做一下讨论: - 如果没有更新,啥也不用管。 - 如果更新了,就说明**当前**这个点是重要的城市; - ``ans[s][e] = x;`` - 如果相等,说明有别的路等价于**当前**最短路,之前重要的城市其实不是重要的城市 - ``ans[s][e] = 0;`` 最后统计一下答案 ### Code ```cpp #include <iostream> #include <cstdio> #include <algorithm> #include <cmath> using namespace std; typedef long long ll; ll n,m,dis[205][205]; ll ans[205][205]; bool vis[205],f = 0; int main(){ cin >> n >> m; for(int i = 1;i <= n;i++){ for(int j = 1;j <= n;j++){ dis[i][j] = 1e18; if(i == j) dis[i][j] = 0; } } while(m--){ ll s,e,x; cin >> s >> e >> x; dis[s][e] = x; dis[e][s] = x; } for(int x = 1;x <= n;x++){ for(int s = 1;s <= n;s++){ for(int e = 1;e <= n;e++){ if(s == e || s == x || x == e) continue ; ll N = dis[s][x] + dis[x][e]; if(dis[s][e] > N){ dis[s][e] = N; ans[s][e] = x; }else if(dis[s][e] == N){ ans[s][e] = 0; } } } } for(int s = 1;s <= n;s++){ for(int e = 1;e <= n;e++){ vis[ans[s][e]] = 1; } } for(int i = 1;i <= n;i++){ if(vis[i]){ cout << i << " "; f = 1; } } if(!f){ cout << "No important cities."; } return 0; } ```