闲来无事,复习一下最小生成树的题目。
2020年5月3日
- A. Jungle Roads
- B. Networking
- C. Building a Space Station
- D. Constructing Roads
- E. QS Network
2020年5月4日
- F. Truck History
- G. Arctic Network
- H. Highways
- I. Agri-Net
A. Jungle Roads
题目就不粘贴了。
一道模板题,可以用prim水过,顺便复习下prim算法。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a > b ? b : a; }
const double eps = 1e-7;
#define endl '\n'
#define pb(a) push_back(a)
#define ALL(x) x.begin(), x.end()
int n;
int g[30][30], d[30];
int vis[30];
int prim() {
int ans = 0;
for (int i = 1; i < n; ++i) {
d[i] = g[0][i]; //初始化距离
vis[i] = 0;
}
d[0] = 0;
for (int i = 1; i < n; ++i) {
int t = -1; //记录最短距离的下标
for (int j = 1; j < n; ++j) {
if (!vis[j] && (t == -1 || d[j] < d[t])) t = j;
}
ans += d[t]; //加上权值
vis[t] = 1;
for (int j = 1; j < n; ++j) {
if (!vis[j]) d[j] = min(d[j], g[t][j]); //更新距离
}
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
while (cin >> n && n) {
memset(g, 0x3f, sizeof(g)); //初始化
for (int i = 0; i < n - 1; ++i) {
char ch, tt;
int q, w;
cin >> ch >> q;
while (q--) {
cin >> tt >> w;
// 把字母转化一下
g[ch - 'A'][tt - 'A'] = g[tt - 'A'][ch - 'A'] = w;
}
}
cout << prim() << endl;
}
return 0;
}
B. Networking
跟第一题一样,模板题。
注意:
输入可能重复,所以相同的要取最小值。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a > b ? b : a; }
const double eps = 1e-7;
#define endl '\n'
#define pb(a) push_back(a)
#define ALL(x) x.begin(), x.end()
int p, r;
int g[55][55];
int vis[55], d[55];
int prim() {
int ans = 0;
for (int i = 2; i <= p; ++i) {
d[i] = g[1][i];
vis[i] = 0;
}
d[1] = 0;
for (int i = 2; i <= p; ++i) {
int t = -1;
for (int j = 2; j <= p; ++j) {
if (!vis[j] && (t == -1 || d[j] < d[t])) t = j;
}
ans += d[t];
vis[t] = 1;
for (int j = 2; j <= p; ++j) {
if (!vis[j]) d[j] = min(d[j], g[t][j]);
}
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
while (cin >> p && p) {
cin >> r;
memset(g, 0x3f, sizeof(g));
while (r--) {
int u, v, w;
cin >> u >> v >> w;
//取最小值
if (w < g[u][v]) {
g[u][v] = g[v][u] = w;
}
}
cout << prim() << endl;
}
return 0;
}
C. Building a Space Station
这题需要转换一下,告诉你坐标和半径,然后求最小生成树。
首先需要把每条边的权值算出来,结合样例可以知道,假设有两个圆心
A
(
x
1
,
y
1
,
z
1
)
A(x_1,y_1,z_1)
A(x1,y1,z1),半径为
r
1
r_1
r1,
B
(
x
2
,
y
2
,
z
2
)
B(x_2,y_2,z_2)
B(x2,y2,z2),半径为
r
2
r_2
r2,那么
A
B
AB
AB边的权值是
d
i
s
(
A
B
)
−
(
r
1
+
r
2
)
dis(AB)-(r_1+r_2)
dis(AB)−(r1+r2),其中
d
i
s
(
A
B
)
dis(AB)
dis(AB)为
A
B
AB
AB两点的距离。
其他就都是一样的。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int max(int a, int b) { return a > b ? a : b; }
double min(double a, double b) { return a > b ? b : a; }
const double eps = 1e-7;
#define endl '\n'
#define pb(a) push_back(a)
#define ALL(x) x.begin(), x.end()
#define sb(x) (x * x)
int n;
//坐标和半径
struct node {
double x, y, z, r;
} sp[110];
int vis[110];
double d[110], g[110][110];
//返回权值
double dist(node a, node b) {
return sqrt(sb((a.x - b.x)) + sb((a.y - b.y)) + sb((a.z - b.z))) -
(a.r + b.r);
}
double prim() {
double ans = 0.0;
for (int i = 1; i < n; ++i) {
d[i] = g[0][i];
vis[i] = 0;
}
d[0] = 0.0;
for (int i = 1; i < n; ++i) {
int t = -1;
for (int j = 1; j < n; ++j) {
if (!vis[j] && (t == -1 || d[j] < d[t])) t = j;
}
ans += d[t];
vis[t] = 1;
for (int j = 1; j < n; ++j) {
if (!vis[j]) d[j] = min(d[j], g[t][j]);
}
}
return ans;
}
int main() {
// ios::sync_with_stdio(false);
while (~scanf("%d", &n) && n) {
for (int i = 0; i < n; ++i) {
scanf("%lf%lf%lf%lf", &sp[i].x, &sp[i].y, &sp[i].z, &sp[i].r);
}
for (int i = 0; i < n; ++i) {
for (int j = i; j < n; ++j) {
double tmp = dist(sp[i], sp[j]);
if (i == j || tmp <= 0) //如果出现圆相交或者重合的情况,就不需要权值了
g[i][j] = g[j][i] = 0;
else
g[i][j] = g[j][i] = tmp;
}
}
printf("%.3f\n", prim());
}
return 0;
}
D. Constructing Roads
给你任意两点的权值,和已经存在的 Q Q Q条路,然后求最小生成树。可以用 k r u s k a l kruskal kruskal算法来做。还是模板题。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a > b ? b : a; }
const double eps = 1e-7;
#define endl '\n'
#define pb(a) push_back(a)
#define ALL(x) x.begin(), x.end()
//边
struct Edge {
int from, to, w; //起点,终点,权值
bool operator<(const Edge& a) const { return w < a.w; }
} edge[110 * 110];
int fa[110]; //点的爸爸
int n, cnt;
//并查集的操作
int Find(int x) { return x == fa[x] ? x : fa[x] = Find(fa[x]); }
void un(int u, int v) {
int pu = Find(u), pv = Find(v);
if (pu != pv) fa[pu] = pv;
}
int kruskal() {
int ans = 0;
sort(edge, edge + cnt);
for (int i = 0; i < cnt; ++i) {
int pa = Find(edge[i].from), pb = Find(edge[i].to);
if (pa != pb) { //没有联通
fa[pa] = pb;
ans += edge[i].w;
}
}
return ans;
}
int main() {
// ios::sync_with_stdio(false);
while (~scanf("%d", &n)) {
cnt = 0; //边的编号
for (int i = 1; i <= n; ++i) {
fa[i] = i;
for (int j = 1; j <= n; ++j) {
scanf("%d", &edge[cnt].w);
edge[cnt].from = i, edge[cnt].to = j;
cnt++;
}
}
int q;
scanf("%d", &q);
while (q--) {
int u, v;
scanf("%d%d", &u, &v);
un(u, v); //先修Q条路
}
printf("%d\n", kruskal());
}
return 0;
}
E. QS Network
每条边的权值 = 每条边的价格 + 边的两个顶点的适配器价格
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a > b ? b : a; }
const double eps = 1e-7;
#define endl '\n'
#define pb(a) push_back(a)
#define ALL(x) x.begin(), x.end()
struct Edge {
int from, to, w;
bool operator<(const Edge& a) const { return w < a.w; }
} e[N];
int fa[1010];
int pp[1010];
int T, n, cnt;
int Find(int x) { return x == fa[x] ? x : fa[x] = Find(fa[x]); }
int kruskal() {
int ans = 0;
sort(e, e + cnt);
for (int i = 0; i < cnt; ++i) {
int pa = Find(e[i].from), pb = Find(e[i].to);
if (pa != pb) {
fa[pa] = pb;
ans += e[i].w;
}
}
return ans;
}
int main() {
// ios::sync_with_stdio(false);
scanf("%d", &T);
while (T--) {
cnt = 0;
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
fa[i] = i;
scanf("%d", pp + i);
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
scanf("%d", &e[cnt].w);
e[cnt].w += pp[i] + pp[j]; //权值
e[cnt].from = i, e[cnt].to = j;
cnt++;
}
}
printf("%d\n", kruskal());
}
return 0;
}
F. Truck History
题目已经给出了公式,所求的是 1 ∑ ( t 0 , t d ) d ( t 0 , t d ) \frac 1{\sum(t_0,t_d)d(t_0,t_d)} ∑(t0,td)d(t0,td)1,要这玩意最大,不就是要分母最小。然后给你几串字符串,把权值当成两个字符串中不相等的字符的个数就行了。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a > b ? b : a; }
const double eps = 1e-7;
#define endl '\n'
#define pb(a) push_back(a)
#define ALL(x) x.begin(), x.end()
int n;
int g[2010][2010];
int d[2010], vis[2010];
string s[2010];
int get_d(string &s1, string &s2) {
int cnt = 0;
for (int i = 0; i < 7; ++i) cnt += (s1[i] != s2[i]);
return cnt;
}
int prim() {
int ans = 0;
for (int i = 0; i < n; ++i) {
d[i] = g[0][i];
vis[i] = 0;
}
d[0] = 0;
for (int i = 0; i < n; ++i) {
int t = -1;
for (int j = 0; j < n; ++j) {
if (!vis[j] && (t == -1 || d[j] < d[t])) t = j;
}
vis[t] = 1;
if (i) ans += d[t];
for (int j = 0; j < n; ++j) {
if (!vis[j]) d[j] = min(d[j], g[t][j]);
}
}
return ans;
}
int main() {
// ios::sync_with_stdio(false);
while (~scanf("%d", &n) && n) {
memset(d, 0x3f, sizeof(d));
for (int i = 0; i < n; ++i) {
cin >> s[i];
}
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
g[i][j] = g[j][i] = get_d(s[i], s[j]);
}
}
printf("The highest possible quality is 1/%d.\n", prim());
}
return 0;
}
G. Arctic Network
给你 P P P个点,每两个点的权值就是他们的距离,然后可以去掉 S − 1 S-1 S−1条边,问你最小生成树。把模板变一下就行了,求和那里改成把权值储存下来,然后降序排,输出下标为 S − 1 S-1 S−1就行。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a > b ? b : a; }
const double eps = 1e-7;
#define endl '\n'
#define pb(a) push_back(a)
#define ALL(x) x.begin(), x.end()
#define sb(x) (x * x)
int T, s, n;
double g[510][510];
double d[510];
int vis[510];
struct point {
int x, y;
} a[510];
double dis(point u, point v) { return sqrt(sb((u.x - v.x)) + sb((u.y - v.y))); }
bool cmp(int a, int b) { return a > b; }
void prim() {
vector<double> ans;
for (int i = 1; i < n; ++i) {
d[i] = g[0][i];
vis[i] = 0;
}
d[0] = 0;
for (int i = 1; i < n; ++i) {
int t = -1;
for (int j = 1; j < n; ++j) {
if (!vis[j] && (t == -1 || d[t] > d[j])) t = j;
}
vis[t] = 1;
ans.push_back(d[t]);
for (int j = 1; j < n; ++j) {
if (!vis[j]) d[j] = min(d[j], g[t][j]);
}
}
sort(ALL(ans), cmp);
printf("%.2f\n", ans[s - 1]);
}
int main() {
// ios::sync_with_stdio(false);
scanf("%d", &T);
while (T--) {
scanf("%d%d", &s, &n);
memset(g, 0x3f, sizeof(g));
for (int i = 0; i < n; ++i) {
scanf("%d%d", &a[i].x, &a[i].y);
}
for (int i = 0; i < n; ++i) {
for (int j = i; j < n; ++j) {
g[i][j] = g[j][i] = (i == j ? 0 : dis(a[i], a[j]));
}
}
prim();
}
return 0;
}
H. Highways
kruskal模板题,水。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a > b ? b : a; }
const double eps = 1e-7;
#define endl '\n'
#define pb(a) push_back(a)
#define ALL(x) x.begin(), x.end()
#define sb(x) (x * x)
struct Edge {
int from, to;
int w;
bool operator<(const Edge& a) const { return w < a.w; }
} e[760 * 760];
struct Point {
int x, y;
} a[1000];
int n, cnt;
int fa[1010];
int getd(Point A, Point B) { return sb((A.x - B.x)) + sb((A.y - B.y)); }
int Find(int x) { return x == fa[x] ? x : fa[x] = Find(fa[x]); }
void un(int u, int v) {
int pa = Find(u), pb = Find(v);
if (pa != pb) fa[pa] = pb;
}
void kruskal() {
for (int i = 1; i <= n; ++i) {
for (int j = i + 1; j <= n; ++j) {
e[cnt].w = getd(a[i], a[j]);
e[cnt].from = i, e[cnt].to = j;
cnt++;
}
}
sort(e, e + cnt);
for (int i = 0; i < cnt; ++i) {
int pa = Find(e[i].from), pb = Find(e[i].to);
if (pa != pb) {
fa[pa] = pb;
printf("%d %d\n", e[i].from, e[i].to);
}
}
}
int main() {
// ios::sync_with_stdio(false);
scanf("%d", &n);
cnt = 0;
for (int i = 1; i <= n; ++i) {
fa[i] = i;
scanf("%d%d", &a[i].x, &a[i].y);
}
int m;
scanf("%d", &m);
while (m--) {
int u, v;
scanf("%d%d", &u, &v);
un(u, v);
}
kruskal();
return 0;
}
I. Agri-Net
模板题。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a > b ? b : a; }
const double eps = 1e-7;
#define endl '\n'
#define pb(a) push_back(a)
#define ALL(x) x.begin(), x.end()
int n;
int g[110][110];
int d[110];
int vis[110];
int prim() {
int ans = 0;
for (int i = 2; i <= n; ++i) {
vis[i] = 0;
d[i] = g[1][i];
}
d[1] = 0;
for (int i = 2; i <= n; ++i) {
int t = -1;
for (int j = 2; j <= n; ++j) {
if (!vis[j] && (t == -1 || d[j] < d[t])) t = j;
}
vis[t] = 1;
ans += d[t];
for (int j = 1; j <= n; ++j) {
if (!vis[j]) d[j] = min(d[j], g[t][j]);
}
}
return ans;
}
int main() {
// ios::sync_with_stdio(false);
while (~scanf("%d", &n) && n) {
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
scanf("%d", &g[i][j]);
}
}
cout << prim() << endl;
}
return 0;
}