但此時有個問題,若存在 k 個關鍵邊,則代表存在 k 個孤立點,且由於原圖是連通的,因此剩下 n−k 個點要構成連通分量。為方便說明,以下令 x=n−k。
由於給定的圖必須是簡單圖,不能存在重複的邊,因此這個連通分量中最多只能有 2x×(x−1) 條邊,因此若剩餘的邊數 m−k≤2x×(x−1),則代表可以存在 k 個關鍵邊,反之則無法存在 k 個關鍵邊。
解法一:貪婪(Greedy) + 暴力枚舉(Brute Force)
由於目標是找到最多的關鍵邊,因此我們可以從 k=n−1 開始枚舉,直到找到最大的 k 使得 2x×(x−1)≥m−k 即可。
在枚舉 k 時有個小地方要注意,關鍵邊的數量最多是 n−1 條,而不是 n 條,可以自行思考為什麼。
時間複雜度:O(n),對於每個詢問。
空間複雜度:O(1)。
1 2 3 4 5 6 7 8 9 10
T = int(input())
for _ inrange(T): n, m = map(int, input().split()) # n vertices, m edges for k inrange(n-1, -1, -1): # 枚舉關鍵點的數量 k ,從 n-1 開始 x = n - k # 剩餘x個點,是否可以構成連通分量 # k條邊使k個關鍵點連接到該強連通分量,剩餘 m-k 邊皆屬於該強連通分量,該強連通分量最多可以有 x*(x-1)/2 條邊 if x * (x-1) // 2 >= m - k: print(k) break
#include<bits/stdc++.h> usingnamespace std; using LL = longlong; #define endl '\n'
intmain(){ ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); LL t, n, m; cin >> t; while (t--) { cin >> n >> m; for (LL k = n-1; k >= 0; --k) { LL x = n - k; if (x * (x-1) / 2 >= m - k) { cout << k << endl; break; } } } return0; }
defcheck(k): x = n - k return x * (x-1) // 2 >= m - k
T = int(input()) for _ inrange(T): n, m = map(int, input().split()) # n vertices, m edges left, right = 0, n-1# [0, n-1] while left <= right: mid = (left + right) // 2 if check(mid): left = mid + 1 else: right = mid - 1 print(right)
#include<bits/stdc++.h> usingnamespace std; using LL = longlong; #define endl '\n'
intmain(){ ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); LL t, n, m; cin >> t; while (t--) { LL n, m, x, k; cin >> n >> m; LL left = 0, right = n-1; while (left <= right) { k = (left + right) / 2; // mid x = n - k; if (x * (x-1) / 2 >= m - k) left = k + 1; else right = k - 1; } cout << right << endl; } return0; }