无向图最小环

Link-Cut Tree (nowcoder.com)

题意:

无向图输出最小环上的边

思路:

可以按权值从小到大加边,直到出现环,之后dfs找环

实现:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <map>
#include <set>
#include <climits>
#include <unordered_map>
using namespace std;
#define mst(x, y) memset(x, y, sizeof x)
#define X first
#define Y second
#define int long long
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
const int N = 200010, INF = 0x3f3f3f3f3f3f3f3f, MOD = 1e9 + 7;
const double EPS = 1e-6;
typedef pair<int, int> PII;
typedef unordered_map<int, int> Ump;
int T;
int n, m, a[N];
int p[N];
int u, v;
vector<PII> g[N];
int ans[N], top;
int find(int x) // 并查集
{
    if (p[x] != x)
        p[x] = find(p[x]);
    return p[x];
}
void dfs(int u, int fa)
{
    cout << u << ' ' << fa << endl;
    if (u == v)
    {
        sort(ans, ans + top);
        for (int i = 0; i < top; i++)
            cout << ans[i] << ' ';
        return;
    }
    for (auto t : g[u])
    {
        if (t.X == fa)
            continue;
        ans[top++] = t.Y;
        dfs(t.X, u);
        top--;
    }
}
void solve()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        g[i].clear();
        p[i] = i;
    }

    bool f = false;
    for (int i = 1; i <= m; i++)
    {
        cin >> u >> v;
        if (f)
            continue;
        int pu = find(u), pv = find(v);
        if (pu != pv)
        {
            g[u].push_back({v, i});
            g[v].push_back({u, i});
            p[pu] = pv;
        }
        else
        {
            top = 0;
            f = true;
            dfs(u, -1);
            cout << i << endl;
        }
    }
    if (!f)
        cout << "-1" << endl;
}
signed main()
{
    FAST;
    cin >> T;
    // T = 1;
    while (T--)
        solve();
    return 0;
}

 

P6175 无向图的最小环问题 - 洛谷

 floyd求最小环

for (int k = 1; k <= n; k++)
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            g[i][j] = min(g[i][j], g[i][k] + g[k][j]);

说明:

第一层循环是枚举的所有中转点 k,然后枚举所有点对(i, j),以 k 为中转点看是否能够更新从 i 到 j 的最短距离。

所以说,当没有枚举到 k 时,g[i,j] 中存的是,从 i 到 j,经过所有编号小于 k 的所有节点的最短距离。节点 k 不在这个路径中。

所以,这时如果我们让节点 k 和节点 i 和节点 j 相连,那么就构成了一个至少为三个节点的环,环中没有重复节点。

这个环的权值就是:此时求得的从 i 到 j 的最短距离 + (i,k)的边权 + (k, j) 的边权,即 g[i,j] + w[i,k] + w[k,j]。枚举所有的点对 (i,j) 取所有这样的环的权值的最小值便是最小环的权值。

所以,我们就在枚举到中转点 k,并且在这个中转点更新其他点对的距离之前进行操作。

实现模板:

 

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <map>
#include <set>
#include <climits>
#include <unordered_map>
using namespace std;
#define mst(x, y) memset(x, y, sizeof x)
#define X first
#define Y second
#define int long long
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
const int N = 200, INF = INT32_MAX, MOD = 1e9 + 7; // 这里INF初始化为更大的LLONG_MAX过不了不知道为什么
const double EPS = 1e-6;
typedef pair<int, int> PII;
typedef unordered_map<int, int> Ump;
int T;
int n, m;
int w[N][N];
int g[N][N];
int res = INF;
void solve()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            if (i != j)
                g[i][j] = w[i][j] = INF;

    for (int i = 1; i <= m; i++)
    {
        int u, v, c;
        cin >> u >> v >> c;
        g[u][v] = g[v][u] = min(g[u][v], c);
        w[u][v] = w[v][u] = min(w[u][v], c);
    }

    for (int k = 1; k <= n; k++)
    {
        for (int i = 1; i < k; i++)
            for (int j = i + 1; j < k; j++)
                res = min(res, g[i][j] + w[i][k] + w[k][j]);

        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                g[i][j] = g[j][i] = min(g[i][j], g[i][k] + g[k][j]);
    }

    if (res == INF)
        cout << "No solution." << endl;
    else
        cout << res << endl;
    // cout << LLONG_MAX << endl;
}
signed main()
{
    FAST;
    // cin >> T;
    T = 1;
    while (T--)
        solve();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值