基本方法网上有, 这里给出证明.
网上的证明基本都是瓶颈生成树, 但是并不能说明
" 任意生成树的第 i 大边不小于最小生成树的第 i 大边 "
证明:
考虑做Kruska
那么一条权值为v的边小于最小生成树里的第k条边
当且仅当只考虑权值小于等于v的边时图上连通块个数大于n-k
那么如果v是某个生成树的第k小边
那小于等于v的边至多把图连成n-k个连通块
(我是说连通块个数小于等于n-k
所以v不可能小于最小生成树的第k小边
(感谢@_rqy)
证毕.
证明了这个结论, 代码就很好写了.
1 #include <cstdio>
2 #include <cstring>
3 #include <iostream>
4 #include <algorithm>
5 #include <vector>
6 #include <cmath>
7 using namespace std;
8 typedef pair<int, int> P;
9 const int MAXN = 500 + 10;
10
11 struct _edge
12 {
13 int from, to; double cost;
14 bool operator <(const _edge &rhs) const{
15 return cost < rhs.cost;
16 }
17 };
18
19 vector<_edge> E;
20 P pos[MAXN];
21 int N, p, S;
22
23 inline double calcdis(P x, P y)
24 {return sqrt(pow(fabs(x.first - y.first), 2) + pow(fabs(x.second - y.second), 2));}
25
26 namespace ufs
27 {
28 int fa[MAXN];
29 inline void init(){for(int i = 1; i <= p; i++) fa[i] = i;}
30 int findfa(int x) {return fa[x] == x ? x : findfa(fa[x]);}
31 inline void unite(int x, int y) {fa[findfa(x)] = findfa(y);}
32 }
33
34 vector<double> ans;
35 void solve()
36 {
37 E.clear();
38 for(int i = 1; i <= p; i++)
39 for(int j = i + 1; j <= p; j++)
40 E.push_back((_edge){i, j, calcdis(pos[i], pos[j])});
41 sort(E.begin(), E.end());
42
43 using namespace ufs;
44
45 init(); ans.clear(); int cnt = 0;
46 for(int i = 0, u, v; i < (int) E.size(); i++)
47 {
48 u = findfa(E[i].from), v = findfa(E[i].to);
49 if(u == v) continue;
50 unite(u, v); cnt++;
51 ans.push_back(E[i].cost);
52 if(cnt == p - 1) break;
53 }
54 printf("%.2lf
", ans[ans.size() - S]);
55 }
56
57 int main()
58 {
59 //freopen("10369.in", "r", stdin);
60 //freopen("10369.out", "w", stdout);
61 cin>>N;
62 while(N--)
63 {
64 cin>>S>>p;
65 for(int i = 1, x, y; i <= p; i++)
66 {
67 scanf("%d%d", &x, &y);
68 pos[i] = P(x, y);
69 }
70 solve();
71 }
72 return 0;
73 }
第N次把kruskal的
u = findfa(E[i].from), v = findfa(E[i].to);
写成
u = E[i].from, v = E[i].to;
, 气啊.