求多边形重心HDU-1115 Lifting the Stone
解题思路
1.设三角形三个顶点 x 1 , x 2 , x 3 x_1,x_2,x_3 x1,x2,x3,重心坐标为 x 1 + x 2 + x 3 3 \frac{x_1+x_2+x_3}{3} 3x1+x2+x3。
2.求多边形的重心可以对每个三角形的有向面积求加权平均,那有向面积裆燃是用叉积求喽。
注意事项
1.不用每一步都做除法。最后除 6 6 6 即可。
6 = 2 × 3 6 = 2 × 3 6=2×3 解释:每一步计算三角形的有向面积的时候要除以 2 2 2,计算三角形重心坐标要除以 3 3 3。
2.有向面积有正负,不能取绝对值。
3.题目是说要四舍五入保留两位小数,但是我加 0.005 0.005 0.005 之后不太对劲,咋回事?
我的妈呀原来 printf("%.2f", x)
会根据要保留的位数自动进行四舍五入的操作…
我直接大吃一惊我仿佛是个假人
赶紧学一下:
如上,如果不想让其四舍五入:
#include <stdio.h>
#include <math.h>
int main()
{
double d = 1.199;
printf("%.2f", floor(d * 100) / 100);
return 0;
}
参考代码
#include<bits/stdc++.h>
using namespace std;
#define LOCAL //提交的时候一定注释
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define VI vector<int>
#define INF 0x3f3f3f3f
#define mp make_pair
#define lowbit(x) ((x) & (-x))
typedef long long LL;
typedef double db;
const db eps = 1e-6; //定义浮点数误差
const int MOD = 998244353;
const int maxn = 1000010;
int readint(){
int x; scanf("%d", &x); return x;
}
//计算几何
int sgn(db x) {
//判断浮点数是否为0,为0时返回0
if (fabs(x) < eps) return 0;
return x < 0 ? -1 : 1;
}
struct point{
db x, y;
point(db X = 0, db Y = 0) {
x = X, y = Y;}
point operator + (point B) {
return point{
x + B.x, y + B.y};}
point operator - (point B) {
return point{
x - B.x, y - B.y};}
point operator / (db k) {
return point{
x / k, y / k};}
point operator * (db k) {
return point{
x * k, y * k};}
bool operator == (point B) {
return sgn(x - B.x) == 0 && sgn(y - B.y) == 0;}
}p[maxn];
db cross(point A, point B) {
return A.x * B.y - A.y * B.x;}//求×积
db distance(point A, point B) {
return hypot(A.x - B.x, A.y - B.y);}
db polygon_area(point *p, int n) {
//求多边形的面积
db area = 0;
_for(i, 0, n) {
area += cross(p[i], p[(i + 1)%n]);
}
return area/2; //是按照三角形来算,所以这里统一除2
}
point polygon_center(point *p, int n) {
point ans(0, 0);
db area = polygon_area(p, n);
if (area == 0) return ans;
_for(i, 0, n) {
ans = ans + (p[i] + p[(i + 1)%n]) * cross(p[i], p[(i + 1)%n]);
}
return ans/area/6;
}
int main() {
#ifdef LOCAL
freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
#endif
int t, n;
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
_for(i, 0, n) {
scanf("%lf%lf", &p[i].x, &p[i].y);
}
point ans = polygon_center(p, n);
printf("%.2f %.2f\n", ans.x, ans.y); //不用加0.005
}
return 0;
}
判断线段相交+并查集HDU-1558 Segment set
解题思路
读入线段,判断线段是否于已有的线段相交,用并查集维护某个线段所在集合的总个数。
注意事项
1.注意当前线段对应的根节点 r 1 r1 r1 是会更新的,所以每次要重新查询,一开始的时候写在循环外面了,导致错误。
2.输出个数:There is a blank line between test cases.
注意输出不同case
之间空一行。
3.注意并查集的巧妙写法。
对集合 s s s 的根节点来说, s [ r o o t ] = − n s[root] = -n s[root]=−n ,其绝对值 n n n 为集合元素个数。
其余节点 o t h e r other other , s [ o t h e r ] = r o o t s[other] = root s[other]=root ,对应的值根节点的序号。
故可以根据 s [ i ] s[i] s[i] 的值是否是负数来判断是否为集合的根节点。
这一性质运用于集合的合并当中,显得很优雅:
void union_set(int r1, int r2) {
//合并根节点为r1,r2的两个集合
if (s[r1] > s[r2]) {
//注意二者均为负数,说明r1的元素少
s[r2] += s[r1]; //一定先更新元素的个数
s[r1] = r2; //再更新对应的根节点
} else {
s[r1] += s[r2];
s[r2] = r1;
}
}
参考代码
#include<bits/stdc++.h>
using namespace std;
#define LOCAL //提交的时候一定注释
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define VI vector<int>
#define INF 0x3f3f3f3f
#define mp make_pair
#define lowbit(x) ((x) & (-x))
typedef long long LL;
typedef double db;
const db eps = 1e-6; //定义浮点数误差
const int MOD = 998244353;
const int maxn = 1020;
int readint(){
int x; scanf("%d", &x); return x;
}
//并查集操作
int s[maxn]; //每个集合的根节点为负数,其绝对值为元素个数,其余节点对应根节点的序号
void init_set() {
fill(s, s + maxn, -1);
}
//路径压缩的递推写法
//防止爆栈
int find_set(int x) {
int r = x; //找根节点
while (s[r] > 0) r = s[r];
int i = x, j; //路径压缩
while (i != r) {
j = s[i];
s[i] = r;
i = j;
}
return r;
}
void union_set(int r1, int r2) {
//合并
if (s[r1] > s[r2]) {
//r1的元素少
s[r2] += s[r1]; //一定先更新
s[r1] = r2;
} else {
s[r1] += s[r2];
s[r2] = r1;
}
}
//计算几何
int sgn(db x) {
//判断浮点数是否为0,为0时返回0
if (fabs(x) < eps) return 0;
return x < 0 ? -1 : 1;
}
struct point{
db x, y;
point(db X = 0, db Y = 0) {
x = X, y = Y;}
point operator + (point B) {
return point{
x + B.x, y + B.y};}
point operator - (point B) {
return point{
x - B.x, y - B.y};}
point operator / (db k) {
return point{
x / k, y / k};}
point operator * (db k) {
return point{
x * k, y * k};}
bool operator == (point B) {
return sgn(x - B.x) == 0 && sgn(y - B.y) == 0;}
};
struct segment{
point a, b;
}seg[maxn];
db cross(point A, point B) {
return A.x * B.y - A.y * B.x;}//求×积
db distance(point A, point B) {
return hypot(A.x - B.x, A.y - B.y);}
//判断两线段相交
bool cross_segment(point a, point b, point c, point d) {
db c1 = cross(b - a, c - a), c2 = cross(b - a, d - a);
db d1 = cross(d - c, a - c), d2 = cross(d - c, b - c);
return sgn(c1 * c2) <= 0 && sgn(d1 * d2) <= 0;
}
int main() {
#ifdef LOCAL
freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
#endif
int t, n;
char ch[10];
db x1, y1, x2, y2;
scanf("%d", &t);
_for(i, 0, t) {
if (i) printf("\n");
init_set();
scanf("%d", &n);
int cnt = 1;
_for(i, 0, n) {
scanf("%s", ch);
if (ch[0] == 'P') {
scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
seg[cnt] = segment{
point(x1, y1), point(x2, y2)};
_rep(j, 1, cnt - 1) {
if (cross_segment(seg[j].a, seg[j].b, seg[cnt].a, seg[cnt].b)) {
int r1 = find_set(cnt);
int r2 = find_set(j);
if (r1 != r2) {
union_set(r1, r2);
}
}
}
cnt++;
} else {
int pos = readint();
printf("%d\n", abs(s[find_set(pos)]));
}
}
}
return 0;
}