等腰三角形
题目
题意:
根据给定坐标判断能组成多少个等腰三角形。
思路
任选一个点作为等腰三角形的顶点,与其相连的两条边作为两个腰,这样我们就可以枚举所有点作为顶点,然后计算以该顶点为端点的所有边长度,统计所有长度相等的边的数量即为组成三角形的数量。
统三角形计数量:每次先加上对应长度的边的数量,在将对应长度的 cnt 计数加1。原理:任意两条相等边都可以组成一个三角形,则为
C
2
n
C^n_2
C2n,刚好等于
1
+
2
+
3
+
.
.
.
+
(
n
−
1
)
1 + 2 + 3 + ... + (n - 1)
1+2+3+...+(n−1)(等差数列求和
(
n
−
1
)
(
1
+
n
−
1
)
2
=
n
(
n
−
1
)
2
=
C
2
n
\frac{(n - 1)(1 + n - 1)}{2} = \frac{n(n - 1)}{2} = C^n_2
2(n−1)(1+n−1)=2n(n−1)=C2n),则答案可以先加上对应长度的 cnt,再将对应长度的 cnt 计数加1。这样如果有 n 条边相等,则计数一共是
1
+
2
+
.
.
.
+
(
n
−
1
)
1 + 2 + ... + (n - 1)
1+2+...+(n−1)。
对于共线问题:以 A 为顶点,对于AB如果有一条长度相等的边与其共线的话,则位置应该在其关于 A 点的反向延长线上,可以通过计算判断是否存在。
vis[nodes[i].x - (nodes[j].x - nodes[i].x) + 1500][nodes[i].y - (nodes[j].y - nodes[i].y) + 1500]
代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
int cnt[2110000]; // 1000 的平方 + 1000 的平方
int vis[5000][5000];
struct node {
int x, y;
}nodes[3010];
int dist(node a, node b) {
return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i ++) {
int x, y;
cin >> x >> y;
nodes[i].x = x;
nodes[i].y = y;
vis[x + 1500][y + 1500] = 1;
}
int ans = 0, line = 0;
for (int i = 0; i < n; i ++) { // 每一次选一个点作为顶点
for (int j = 0; j < n; j ++) {
if (i != j) {
ans += cnt[dist(nodes[i], nodes[j])];
cnt[dist(nodes[i], nodes[j])] ++;
if (vis[nodes[i].x - (nodes[j].x - nodes[i].x) + 1500][nodes[i].y - (nodes[j].y - nodes[i].y) + 1500])
line ++;
}
}
for (int j = 0; j < n; j ++)
cnt[dist(nodes[i], nodes[j])] = 0;
}
cout << ans - line / 2; // 枚举每个点,计算共线的时候每个点会计算两次 B->A->C C->A->B B、C各计算两次。
return 0;
}
复杂度
- 时间复杂度: O ( n 2 ) O(n^2) O(n2)。
- 空间复杂度:
很多。
旅游
题目
大意就是找出最小的 p 值,使得牛牛能用自己的钱修权值大于 p 路使整个图连通。
思路
对于 p 值,我们在区间[0, max_val]
内进行二分,其中最大值由每条边的权值的最大值决定。对于每一个二分的 p 值,判断牛牛是否能用自己的钱使图连通。
判断方法,遍历所有的边:
- 使用并查集,并且维护每一个集合的
size
,对于边的权值小于 p 的,直接合并为一个集合并更新集合的size
; - 对于道路权值大于 p 的,如果边的两端不在一个集合里,则将其权值加到数组里(这个数组是专门用来存储牛牛需要花钱修的路的权值),一直遍历到所有节点都在一个集合(即整张图连通,
size = n
),此时在计算数组里的路所需要的维修费用。
维修费用的计算:因为每条路的费用与第几次维修有关,所以采用贪心的思想,先修贵的,即将数组排序并翻转,然后计算总的维修费用,如果小于 c
,则说明当前 p
满足要求,返回 true
。
代码
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <unordered_map>
#include <vector>
#include <string>
#include <cmath>
#include <algorithm>
#include <limits.h>
#include <map>
#include <unordered_set>
using namespace std;
struct node {
int x, y, w;
};
int cmp(node a, node b) {
return a.w < b.w;
}
long long n, m, c;
vector<node> nodes(100010);
int f[40010];
int cnt[40010];
int find(int a) {
if (a != f[a])
f[a] = find(f[a]);
return f[a];
}
bool solve(int p) {
vector<int> ans;
long long sum = 0;
for (int i = 1; i <= n; i ++) {
f[i] = i;
cnt[i] = 1;
}
int pos = 0;
for (int i = 0; i < m; i ++)
if (nodes[i].w <= p) {
int x = find(nodes[i].x);
int y = find(nodes[i].y);
if (x != y) {
cnt[x] += cnt[y];
f[y] = x;
}
pos = i;
}
else {
int x = find(nodes[i].x);
int y = find(nodes[i].y);
if (x != y) {
ans.push_back(nodes[i].w);
cnt[x] += cnt[y];
f[y] = x;
}
if (cnt[x] == n) {
for (int j = 0; j < ans.size(); j ++) {
sort(ans.begin(), ans.end());
reverse(ans.begin(), ans.end());
sum += (j + 1) * ans[j];
}
if (sum <= c)
return true;
break;
}
}
return false;
}
int main() {
cin >> n >> m >> c;
int t = 0;
for (int i = 0; i < m; i ++) {
int x, y, w;
scanf("%d%d%d", &x, &y, &w);
nodes[i] = {x, y, w};
t = max(t, w);
}
sort(nodes.begin(), nodes.begin() + m, cmp);
int l = 0, r = t;
while (l < r) {
int mid = l + r >> 1;
if (solve(mid))
r = mid;
else
l = mid + 1;
}
cout << l;
return 0;
}