1013 Battle Over Cities (25 分)
- 题意 :给图,问去掉所询问的一个点后,需要添加多少条边可以使图连通, N < 1000 N<1000 N<1000
- 思路 :并查集,将每条边存储,每次删掉一个点时,相当于将与这个点相关的边与这个点本身都不加载在图中,每次重新建图,查询每次新图的连通块个数,加上连通块个数-1的边就可以使图连通
- 思路 :连通块个数的计算,可以用递减的方式计算,一开始连通块个数等于图上点的个数n - 1,每加入一条边,如果这条边的两个端点原先不在同一个连通块内,则连通块个数-1,否则,continue
- 语法 :无向图的最大边数 M = ( N − 1 ) + ( N − 2 ) + . . . + 1 = N ∗ ( N − 1 ) 2 = 5 ∗ 1 0 5 M = (N - 1) +(N -2)+...+1=\frac{N*(N-1)}{2}=5*10^5 M=(N−1)+(N−2)+...+1=2N∗(N−1)=5∗105
#include <iostream>
#include <vector>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
#define endl '\n'
#define pb push_back
#define fi first
#define se second
const int N = 1010, M = 5e5 + 10;
int fa[N];
struct Edge
{
int u, v;
}e[M];
int find(int x)
{
if (fa[x] != x) fa[x] = find(fa[x]);
return fa[x];
}
int main()
{
cin.tie(nullptr) -> sync_with_stdio(false);
int n, m, k; cin >> n >> m >> k;
for (int i = 0; i < m; i ++ ) cin >> e[i].u >> e[i].v;
while (k -- )
{
int x; cin >> x;
for (int i = 1; i <= n; i ++ ) fa[i] = i;
int cnt = n - 1;
for (int i = 0; i < m; i ++ )
{
int u = e[i].u, v = e[i].v;
if (u != x && v != x)
{
u = find(u), v = find(v);
if (u != v)
{
cnt -- ;
fa[u] = v;
}
}
}
cout << cnt - 1 << endl;
}
}
1114 Family Property (25 分)
题意 :
- 输入每个人的id和父亲母亲(去世则输入-1)所有孩子和房产数量和房产总面积
- 输出每个家庭的id(家庭成员中最小的编号),人数,人均房产数量,人均房产面积
- 按人均房产面积降序顺序输出所有家庭信息。当存在人均房产面积相同的情况时,按 ID 升序顺序排序。
思路 :
- 先读入每个人的信息,再根据存下来的每条边,利用并查集,将连通块内的人的信息合并在连通块内编号最小的人身上
- 然后遍历人,找到每个连通块内编号最小的人(也就是家主),这一步需要之前输入信息时,记录哪些人是连通块内存在的
语法 :
- 自定义排序时,尽量避免用除法,将出发转换为乘法
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 1e4 + 10;
typedef pair<int, int> PII;
int n;
int hc[N], ha[N];
bool st[N];
PII e[N * 2];
int m;
int pa[N];
int c[N];
struct Family
{
int id, c, hc, ha;
bool operator< (const Family &t) const
{
// ha / c > t.ha / t.c
if (ha * t.c != t.ha * c) return ha * t.c > t.ha * c;
return id < t.id;
}
};
int find(int x)
{
if (pa[x] != x) pa[x] = find(pa[x]);
return pa[x];
}
int main()
{
scanf("%d", &n);
int id, fa, mo, k, son;
for (int i = 1; i <= n; i ++ )
{
scanf("%d%d%d%d", &id, &fa, &mo, &k);
st[id] = true;
if (fa != -1) e[m ++ ] = {id, fa}, st[fa] = true;
if (mo != -1) e[m ++ ] = {id, mo}, st[mo] = true;
for (int j = 1; j <= k; j ++ )
{
scanf("%d", &son);
e[m ++ ] = {id, son};
st[son] = true;
}
scanf("%d%d", &hc[id], &ha[id]);
}
for (int i = 0; i < N; i ++ ) pa[i] = i, c[i] = 1;
for (int i = 0; i < m; i ++ )
{
int a = e[i].first, b = e[i].second;
a = find(a), b = find(b);
if (a != b)
{
if (a > b) swap(a, b);
hc[a] += hc[b];
ha[a] += ha[b];
c[a] += c[b];
pa[b] = a;
}
}
vector<Family> family;
for (int i = 0; i < N; i ++ )
{
if (st[i] && pa[i] == i)
family.push_back({i, c[i], hc[i], ha[i]});
}
sort(family.begin(), family.end());
printf("%d\n", family.size());
for (auto &f : family)
printf("%04d %d %.3lf %.3lf\n", f.id, f.c, (double)f.hc / f.c, (double)f.ha / f.c);
}
1118 Birds in Forest (25 分)
题意 :
- 假设所有出现在同一张照片中的鸟都属于同一棵树。
- 请你帮助科学家计算森林中树木的最大数量,对于任何一对鸟类,请判断它们是否在同一棵树上。
思路 :
- 连通块的数量就是鸟的数量减去被合并的次数
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10010;
int n;
int birds[10];
int p[N];
bool st[N];
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main()
{
scanf("%d", &n);
for (int i = 0; i < N; i ++ ) p[i] = i;
int cnt = 0;
for (int i = 0; i < n; i ++ )
{
int k;
scanf("%d", &k);
for (int j = 0; j < k; j ++ )
{
scanf("%d", &birds[j]);
st[birds[j]] = true;
}
for (int j = 1; j < k; j ++ )
{
int a = birds[j - 1], b = birds[j];
a = find(a), b = find(b);
if (a != b)
{
p[a] = b;
cnt ++ ;
}
}
}
int tot = 0;
for (int i = 0; i < N; i ++ ) tot += st[i];
printf("%d %d\n", tot - cnt, tot);
int q;
scanf("%d", &q);
while (q -- )
{
int a, b;
scanf("%d%d", &a, &b);
if (find(a) == find(b)) puts("Yes");
else puts("No");
}
return 0;
}