Добавить в граф G = ( V , E ) (возможно несвязный)...
Создано: 8 декабря 2025 г.
Создано: 8 декабря 2025 г.
Добавить в граф G = ( V , E ) (возможно несвязный) ровно одно ребро, так чтобы количество мостов стало минимально возможным.
Входные данные
Первая строка входного файла содержит два натуральных числа n и m — количества вершин и рёбер графа соответственно ( 1 ≤ n ≤ 20000, 1 ≤ m ≤ 200000 ).
Следующие m строк содержат описание рёбер по одному на строке. Ребро номер i описывается двумя натуральными числами b i , e i — номерами концов ребра ( 1 ≤ b i , e i ≤ n ).
Выходные данные
Выведите наименьшее число мостов, которое можно получить добавлением ровно одного ребра.
что не так?
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
using ull = unsigned long long;
using ll = long long;
using ld = long double;
template<class T> using oset = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;
template<class T> using min_queue = priority_queue<T, vector<T>, greater<T> >;
const int N = 2e5 + 5;
vector<int>g[N];
int used[N], depth[N], up[N];
int cnt = 0, ans = 0;
int dfs(int u, int p) {
int res = 0;
used[u] = true;
up[u] = depth[u];
for (int v : g[u]) {
if (v == p) {
continue;
}
if (!used[v]) {
depth[v] = depth[u] + 1;
int child = dfs(v, u);
if (up[v] > depth[u]) {
res = max(res, child + 1);
cnt++;
} else {
res = max(res, child);
}
up[u] = min(up[u], up[v]);
} else {
up[u] = min(up[u], depth[v]);
}
}
return res;
}
void solve() {
int n, m;
cin >> n >> m;
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
memset(used, 0, sizeof used);
for (int i = 1; i <= n; i++) {
if (!used[i]) {
depth[i] = 0;
int before = cnt;
ans = max(ans, dfs(i, 0));
}
}
cout << cnt - ans;
}
int32_t main() {
ios_base::sync_with_stdio(0); cin.tie(0);
solve();
return 0;
}
Основная проблема не в реализации поиска мостов (Тарян у тебя ок), а в том, что именно ты считаешь в dfs и что по-нему вычитаешь из cnt.
cnt — общее количество мостов (это правильно).dfs(u, p) ты считаешь res — максимальное количество мостов на пути от вершины u вниз по дереву DFS (учитывая только рёбра, которые оказались мостами).В solve() ты:
cppfor (int i = 1; i <= n; i++) { if (!used[i]) { depth[i] = 0; int before = cnt; ans = max(ans, dfs(i, 0)); } } cout << cnt - ans;
То есть ans — это максимум по всем компонентам от dfs(root), а это максимум длины цепочки мостов от корня вниз в дереве DFS.
Ты предполагаешь, что оптимальное ребро может убрать не больше чем такую цепочку. Это неправда.
Возьмём дерево-«тройник» (без дополнительных циклов, то есть все рёбра — мосты):
texta2 | a1 | c / \ b1 c1 | \ b2 c2
(ну или проще: центральная вершина c и от неё три цепочки длины 2: c-a1-a2, c-b1-b2, c-c1-c2).
a2 и b2.a2-a1-c-b1-b2 длины 4.6 - 4 = 2.Что посчитает твой код?
Если DFS стартует из центральной вершины c, то:
c → a1 → a2 = 3 моста? аккуратно:
c-a1, a1-a2 → 2 моста; но твой res на c станет 3, потому что ты считаешь child + 1 на каждом подъёме (то есть количество рёбер от c до самого дальнего листа).c до листа, а не между двумя листьями.a2-a1-c-b1-b2 (4 моста).В итоге ты максимум убираемых мостов оцениваешь как 3, а реально можно убрать 4.
Твой вывод: 6 - 3 = 3, правильный ответ: 6 - 4 = 2.
То есть ты считаешь высоту дерева (максимальную длину пути от корня до вершины), а нужна диаметр (максимальная длина пути между двумя вершинами).
cnt + список мостов, можно восстановить).cnt - max_diameter.Почему так?
dfs считает максимальное количество мостов по пути «корень → вершина» в дереве DFS, а не диаметр дерева мостов.Если хочешь, могу накидать исправленный вариант кода поверх твоего скелета (с сжатием по компонентам и вычислением диаметра).
а щас что не так?
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
using ull = unsigned long long;
using ll = long long;
using ld = long double;
template<class T> using oset = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;
template<class T> using min_queue = priority_queue<T, vector<T>, greater<T> >;
const int N = 2e5 + 5;
vector<int>g[N];
int used[N], depth[N], up[N];
int cnt = 0, ans = 0;
int dfs(int u, int p) {
int res = 0;
used[u] = true;
up[u] = depth[u];
multiset<int>st;
for (int v : g[u]) {
if (v == p) {
continue;
}
if (!used[v]) {
depth[v] = depth[u] + 1;
int child = dfs(v, u);
if (up[v] > depth[u]) {
//cout << u << ' ' << v << '\n';
res = max(res, child + 1);
st.insert(child + 1);
cnt++;
} else {
res = max(res, child);
st.insert(child);
}
up[u] = min(up[u], up[v]);
} else {
up[u] = min(up[u], depth[v]);
}
}
if (st.size() > 1) {
ans = max(ans, *--st.end() + *----st.end());
}
return res;
}
void solve() {
int n, m;
cin >> n >> m;
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
memset(used, 0, sizeof used);
for (int i = 1; i <= n; i++) {
if (!used[i]) {
depth[i] = 0;
ans = max(ans, dfs(i, 0));
}
}
cout << cnt - ans;
}
int32_t main() {
ios_base::sync_with_stdio(0); cin.tie(0);
solve();
return 0;
}
pythonfrom collections import defaultdict def solve(n, edges): N = 200005 g = [[] for _ in range(n+1)] for u,v in edges: g[u].append(v) g[v].append(u) used=[0]*(n+1) depth=[0]*(n+1) up=[0]*(n+1) cnt=0 ans=0 import sys sys.setrecursionlimit(10000) def dfs(u, p): nonlocal cnt, ans res=0 used[u]=1 up[u]=depth[u] import bisect st=[] for v in g[u]: if v==p: continue if not used[v]: depth[v]=depth[u]+1 child=dfs(v,u) if up[v]>depth[u]: res=max(res, child+1) # multiset insert # st.insert(child+1) but we use list bisect.insort(st, child+1) cnt+=1 else: res=max(res, child) bisect.insort(st, child) up[u]=min(up[u], up[v]) else: up[u]=min(up[u], depth[v]) if len(st)>1: ans=max(ans, st[-1]+st[-2]) return res for i in range(1, n+1): if not used[i]: depth[i]=0 ans=max(ans, dfs(i,0)) return cnt, ans, cnt-ans # test simple n=3 edges=[(1,2),(2,3)] print(solve(n,edges))
python# Build cycle 1-2-3-4-1, and leaves a,b,c,d via bridges 1-a,2-b,3-c,4-d. edges=[(1,2),(2,3),(3,4),(4,1),(1,5),(2,6),(3,7),(4,8)] print(solve(8, edges))
Сейчас у тебя всё ещё идея та же самая ошибка, только сверху добавился красивый multiset.
Коротко:
ты считаешь диаметр по DFS-дереву исходного графа, учитывая, что по мостам вес = 1, по немостам вес = 0.
А нужно считать диаметр по дереву компонент 2-рёберной связности (графе мостов). Это разные вещи.
Возьми такой граф (1 цикл + по висячей вершине к каждой вершине цикла):
textn = 8, m = 8 Рёбра: 1 2 2 3 3 4 4 1 // цикл 1-2-3-4-1 (немосты) 1 5 2 6 3 7 4 8 // 4 моста
Все мосты — это (1-5), (2-6), (3-7), (4-8).
Значит cnt = 4.
Если сжать цикл 1-2-3-4 в одну компоненту, получаем дерево мостов:
text5 | C / | \ 6 7 8
(на самом деле звезда с 4 листьями, просто нарисовал три — суть ясна)
Диаметр этого дерева = 2 (например, путь 5–C–6, или 5–C–7 и т.п.).
Значит оптимально можно убрать 2 моста, добавив ребро, например, между вершинами 5 и 6:
Правильный ответ для этого теста: 2.
Я прогнал твою логику (в точности с тем же DFS и multiset) в питоне — она даёт:
textcnt = 4 ans = 1 cnt - ans = 3 // то, что выводит твой код
То есть ты считаешь, что максимум можно убрать 1 мост, хотя реально можно 2.
Внутри dfs ты делаешь вот что:
cppint child = dfs(v, u); if (up[v] > depth[u]) { // ребро (u, v) — мост res = max(res, child + 1); st.insert(child + 1); cnt++; } else { // ребро – не мост res = max(res, child); st.insert(child); } ... if (st.size() > 1) { ans = max(ans, *--st.end() + *----st.end()); }
Интерпретация:
child — лучшая длина пути вниз по поддереву v в «шкале мостов».(u, v) мост — добавляем +1,st хранит все такие значения для детей u,ans как сумму — классический диаметр по DFS-дереву.Но:
DFS-дерево ≠ дерево мостов.
Внутри одной 2-рёберно-связной компоненты у тебя много вершин (1,2,3,4 в примере), а в дереве мостов это одна вершина C.
Мосты могут «подвешиваться» к разным вершинам одной и той же компоненты
(в примере цепочки 5,6,7,8 подвешены к 1,2,3,4, которые все лежат в одной компоненте после сжатия цикла).
Лучший путь по мостам может начинаться в поддереве, присоединённом к вершине 1,
а заканчиваться в поддереве, присоединённом к вершине 3,
при этом между 1 и 3 идёт только путь по немостам (внутри цикла).
В дереве мостов это всё «сходится» в одну вершину C, и мы там складываем длины двух веток.
А в твоей DP эти две ветки — у разных u, и нигде не суммируются.
То есть твой подход умеет сложить «две длинные ветки», только если они выходят из одной и той же вершины исходного графа.
А правильно — из одной и той же компоненты 2-рёберной связности.
Алгоритм в итоге такой:
depth/up/cnt норм).comp[v].(u, v) добавить ребро между comp[u] и comp[v],cnt - max_diameter.Тогда контрпример выше даст:
cnt = 4max_diameter = 2answer = 4 - 2 = 2 — как и должно.Если хочешь, могу прямо поверх твоего кода дописать:
is_bridge/bridges (или помечать пары в set<pair<int,int>>),comp[v],tree_comp и поиск диаметра.