先放板子:
kruskal: 时间复杂度:
int n, m;
int p[N];
struct Edge
{
int a, b, w;
bool operator< (const Edge &W)const
{
return w < W.w;
}
}edges[M];
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int kruskal()
{
sort(edges, edges + m);
for (int i = 1; i <= n; i ++ ) p[i] = i;
int res = 0, cnt = 0;
for (int i = 0; i < m; i ++ )
{
int a = edges[i].a, b = edges[i].b, w = edges[i].w;
a = find(a), b = find(b);
if (a != b)
{
p[a] = b;
res += w;
cnt ++ ;
}
}
if (cnt < n - 1) return INF;
return res;
}
prim: 时间复杂度:
int n;
int g[N][N];
int dist[N];
bool st[N];
int prim()
{
memset(dist, 0x3f, sizeof dist);
int res = 0;
for (int i = 0; i < n; i ++ )
{
int t = -1;
for (int j = 1; j <= n; j ++ )
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
if (i && dist[t] == INF) return INF;
if (i) res += dist[t];
st[t] = true;
for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], g[t][j]);
}
return res;
}
A,B,D,H,J,K,M
C - Building a Space Station
题意:给n个点的三维坐标,求这n个点构成的最小生成树
计算出n*(n+1)/2条边,跑kruskal即可。(注意精度)
题意:给n个字符串,每个字符串中相同位置不同字符的个数代表两条串的距离,求连接所有字符串的最小路径的倒数。
一样是计算出n*(n+1)/2条边,跑kruskal
F - Arctic Network
题意:有n个点之间需要建立网络,使用两种技术,一种卫星频道,可以无视距离联系,但是个数k有限,一种是无线电,需要两个点距离不超过D才能建立联系,功率越高,D越大,需要确定最小的D值,在图中建立一个生成树。
分析:二分+最小生成树kruskal,对D值二分,建立最小生成树时,在每次加边时额外判断距离是否大于D,最后在判断是否连接了所有点时,在已连接的边数加上k,表示未连接的边使用卫星频道联系,若判断完全连接,即得到最小D值。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1100, M = 2e5+10;
int n, m, k;
int p[N];
struct Edge {
int a, b;
double w;
bool operator< (const Edge &W)const {
return w < W.w;
}
} edges[M];
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
struct Center {
double x, y;
} pos[N];
double kruskal(double d) {
sort(edges, edges + m);
for (int i = 1; i <= n; i ++ ) p[i] = i;
double res = 0;
int cnt = 0;
for (int i = 0; i < m; i ++ ) {
int a = edges[i].a, b = edges[i].b;
double w = edges[i].w;
a = find(a), b = find(b);
if (a != b && d > w) {
p[a] = b;
res += w;
cnt ++ ;
}
}
//cout<<cnt+k-1<<endl;
if (cnt + k - 1 < n - 1) return -1;
return res;
}
inline double mul(Center a, Center b) {
double res = sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
if (res > 0) return res;
else return 0;
}
bool check(double mid) {
if (kruskal(mid)>0) return true;
else return false;
}
int main() {
int T;
cin >> T;
while (T--) {
m = 0;
cin >> k >> n;
for (int i = 1; i <= n; i++) cin >> pos[i].x >> pos[i].y;
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
double w = mul(pos[i], pos[j]);
edges[m] = {i, j, w};
m++;
}
}
double l = 0, r = 10000;
while (r - l > 1e-5) {
double mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
//cout<<mid<<endl;
}
printf("%.2lf\n",l);
//for(int i=0;i<m;i++) cout<<edges[i].a<<" "<<edges[i].b<<" "<<edges[i].w<<'\n';
}
return 0;
}
G - Highways
题意:给n个点的坐标,在部分点之间已经有边,要求建立n个点的最小生成树。
分析:kruskal最小生成树,读入已建成的边时将其加入最小生成树中,再将未建成的边加入树中
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = 3e5 + 10;
typedef pair<int, int> PII;
int n, m, k;
int p[N];
map<PII, int> mp;
vector <PII> v;
struct Edge {
int a, b;
double w;
bool operator< (const Edge &W)const {
return w < W.w;
}
} edges[M];
struct Center {
double x, y;
} pos[N];
inline double mul(Center a, Center b) {
double res = sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
if (res > 0) return res;
else return 0;
}
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
double kruskal() {
sort(edges, edges + m);
double res = 0;
int cnt = 0;
for (int i = 0; i < m; i ++ ) {
int a = edges[i].a, b = edges[i].b;
double w = edges[i].w;
int pa = find(a), pb = find(b);
if (pa != pb) {
p[pa] = pb;
res += w;
cnt ++ ;
v.push_back({a, b});
}
}
if (cnt + k < n - 1) return -1;
return res;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i ++ ) p[i] = i;
for (int i = 1; i <= n; i++) {
cin >> pos[i].x >> pos[i].y;
}
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
double w = mul(pos[i], pos[j]);
edges[m] = {i, j, w};
m++;
}
}
int res = 0;
cin >> k;
for (int i = 0; i < k; i++) {
int a, b;
cin >> a >> b;
int pa = find(a), pb = find(b);
if (pa != pb) {
p[pa] = pb;
res++;
}
}
k = res;
kruskal();
for (int i = 0; i < v.size(); i++) {
cout << v[i].first << " " << v[i].second << endl;
}
return 0;
}
题意:给定一个连接的无向图,告诉它的最小生成树是否是唯一的。
分析:判断最小生成树的唯一性,即如果存在其他最小生成树,说明至少有两条边存在相同权值且连接的点至少有一个相同,在kruskal建立最小生成树时,标记已经使用的边,并记录连接两点的边的权值,建完树后,遍历一遍没有被选用的边,若找到这样的边,说明非唯一。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = 1e5 + 10;
typedef pair<int, int> PII;
int n, m;
int p[N];
int mp[N];
struct Edge {
int a, b, w;
bool operator< (const Edge &W)const {
return w < W.w;
}
} edges[M];
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int fg[N];
int kruskal() {
sort(edges, edges + m);
for (int i = 1; i <= n; i ++ ) p[i] = i;
int res = 0, cnt = 0;
for (int i = 0; i < m; i ++ ) {
int a = edges[i].a, b = edges[i].b, w = edges[i].w;
int pa = find(a), pb = find(b);
if (pa != pb) {
p[pa] = pb;
res += w;
cnt ++ ;
fg[i] = 1;
mp[a]=mp[b]=w;
}
}
if (cnt < n - 1) return -1;
return res;
}
int main() {
int T;
cin >> T;
while (T--) {
memset(fg,0,sizeof(fg));
int flag = 1;
cin >> n >> m;
for (int i = 0; i < m; i++) {
cin >> edges[i].a >> edges[i].b >> edges[i].w;
}
int ans = kruskal();
for (int i = 0; i < m; i++) {
if (!fg[i]) {
int a = edges[i].a, b = edges[i].b, w = edges[i].w;
if (mp[a] == w || mp[b] == w) {
flag = 0;
}
}
}
if (flag) cout << ans << endl;
else cout << "Not Unique!\n";
}
return 0;
}
题意:给定n个点坐标,求最小生成树,对每条边长度要求为大于10,小于1000
分析:在kruskal中额外加一个判断长度的条件即可
N - Qin Shi Huang's National Road System
题意:秦始皇要在n个城市之间建立n-1条路,使它们连接起来,他希望所有道路总长度最小,徐福可以修一条不需要花钱的魔道,秦始皇希望所有非魔法道路总长度越小越好,徐福希望魔法道路让尽可能多的人受益,所以他决定A/B的值必须最大,其中A是魔法道路连接的两个城市的总人口,B是非魔法道路的总长度。
分析:
代码: