TopCoder - 16444 刪邊轉化為加邊 歸納法證明貪心
阿新 • • 發佈:2021-01-08
刪邊轉化為加邊,用並查集維護連通塊直接貪心即可。
具體地:
將
w
w
w從大到小排序依次連入圖中,如果加完某一條邊之後圖聯通了則必須刪除這條邊。
其正確性是可以保障的:
如果加入邊集 E E E後圖連通,那麼一定要在邊集 E E E中刪除若干條邊使得圖不連通。
而我們加入一條邊後圖連通,那麼刪除這條邊一定比刪除在這條邊加入之前加入的邊要優。
但是其後效性是無法保障的:有沒有一種可能刪除先加入的邊後面的邊就可以刪的少了呢?
這樣的方案是有可能的,但一定是不優的。
首先我們來看一個性質:
∀
k
∈
N
,
2
k
>
∑
i
=
0
k
−
1
2
i
\forall k\in \mathbb{N},2^k > \sum_{i=0}^{k-1}2^i
∀k∈N,2k>i=0∑k−12i
那麼因為
∀
1
≤
i
,
j
≤
m
,
w
i
≠
w
j
\forall 1\le i,j \le m,w_i \not= w_j
∀1≤i,j≤m,wi=wj
因此:
2
w
i
>
∑
j
=
i
+
1
m
2
w
j
2^{w_i}>\sum_{j=i+1}^{m}2^{w_j}
2wi>j=i+1∑m2wj
即刪除前面任意一條邊都不如刪除最新加入的一條邊以及所有未加入的邊要優,簡單歸納可證。
#include <iostream>
#include <vector>
#include <algorithm>
const int N = 305,mod = 1e9 + 7;
int fa[N];
typedef std::pair<int,int> PII;
typedef std::pair<int,PII> PIII;
PIII edge[1005];
class TheSocialNetwork{
public:
inline int ksm(int b){
int a = 2,res = 1;
for (;b;b >>= 1){
if (b & 1) res = 1ll * res * a % mod;
a = 1ll * a * a % mod;
}
return res;
}
inline int get(int x){
if (fa[x] == x) return x;
else return fa[x] = get(fa[x]);
}
inline void merge(int x,int y){fa[get(x)] = fa[get(y)];}
int minimumCut(int n, int m, std::vector<int> u, std::vector<int> v, std::vector<int> l){
int cnt = n,ans = 0;
for (int i = 1; i <= n; i++) fa[i] = i;
for (int i = 0; i < m; i++){
edge[i].first = l[i];
edge[i].second.first = u[i];
edge[i].second.second = v[i];
}
std::sort(edge,edge + m);
std::reverse(edge,edge + m);
for (int i = 0; i < m; i++){
l[i] = edge[i].first;
u[i] = edge[i].second.first;
v[i] = edge[i].second.second;
}
for (int i = 0; i < m; i++){
if (get(u[i]) == get(v[i])) continue;
if (cnt > 2){
merge(u[i],v[i]);
cnt--;
}else{
ans = (ans + ksm(l[i])) % mod;
}
}
return ans;
}
};