【POJ1734】Sightseeing trip

题目链接:https://www.acwing.com/problem/content/346/

题目大意:确定无向带权图上至少包含 3 个节点的最小环

solution

一道无向图上的最小环问题 , 考虑 \(Floyd\) , 设 \(i\)\(j\) 间的道路长为 \(f[i][j]\) , 最短路径长 \(g[i][j]\) , 若每次 \(Floyd\) 最外层 遍历到 \(k\) 时 , \(g[i][j]\) 决定了 \(i\)\(j\) 间经过节点编号不超过 \(k\) 的最短路径 , 不妨以 \(k\) 为环上的节点 , 枚举 \(i\)\(j\) , 即是枚举 \(k\) 在环上的相邻节点 , 那么这个环的最小长度为\(f[i][k] + f[k][j] + g[i][j]\) , 这样就可以找出环上节点编号不超过 \(k\) 的最小环 , 由对称性 , 最终可以遍历到每一个最小环

拓展 : 对于有向图上的最小环 , 可以依次枚举起点 \(s\) , 再在 \(dijstra\)\(s\) 向外拓展后 , 把 \(shortestpath[s]\) 设为极大值 , 最终的 \(shortestpath[s]\) 即为从 \(s\) 开始的最小环的长度 , 笔者在这不多加赘述

时间复杂度 : \(O(n^3)\)

code

#include<bits/stdc++.h>
using namespace std;
template <typename T> inline void read(T &FF) {
    int RR = 1; FF = 0; char CH = getchar();
    for(; !isdigit(CH); CH = getchar()) if(CH == '-') RR = -RR;
    for(; isdigit(CH); CH = getchar()) FF = FF * 10 + CH - 48;
    FF *= RR;
}
inline void file(string str) {
    freopen((str + ".in").c_str(), "r", stdin);
    freopen((str + ".out").c_str(), "w", stdout);
}
const int N = 105;
int g[N][N], ans = INT_MAX, f[N][N];
int pos[N][N], n, m;
queue<int> path;
void get_path(int xi, int yi) {
    if(pos[xi][yi] == 0) return;
    get_path(xi, pos[xi][yi]);
    path.push(pos[xi][yi]);
    get_path(pos[xi][yi], yi);
}
int main() {
    //file("");
    int u, v, w;
    read(n), read(m);
    memset(g, 0x3f, sizeof(g));
    memset(f, 0x3f, sizeof(f));
    for(int i = 1; i <= n; i++) f[i][i] = g[i][i] = 0; 
    for(int i = 1; i <= m; i++) {
        read(u), read(v), read(w);
        g[u][v] = g[v][u] = min(g[u][v], w);
        f[u][v] = f[v][u] = g[u][v];
    }
    ans = f[0][0];
    for(int k = 1; k <= n; k++) {
        for(int i = 1; i < k; i++)
            for(int j = i + 1; j < k; j++)
                if((long long)g[i][j] + f[i][k] + f[k][j] < ans) {
                    ans = g[i][j] + f[i][k] + f[k][j];
                    while(!path.empty()) path.pop();
                    path.push(i);
                    get_path(i, j);
                    path.push(j); path.push(k);
                }
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                if(g[i][k] + g[k][j] < g[i][j]) {
                    g[i][j] = g[i][k] + g[k][j];
                    pos[i][j] = k;
                }
    }
    if(path.empty()) {
        puts("No solution.");
        return 0;
    }
    while(!path.empty()) cout << path.front() << " ", path.pop();
    cout << endl;
    return 0;
}


猜你喜欢

转载自www.cnblogs.com/magicduck/p/12241605.html