老是搞不清楚,叉积怎么判断方向的,后来发现就是右手定则的事,看大拇指的方向指向纸面外还是纸面里:
指 向 纸 面 外 ⇔ 叉 积 大 于 0 ⇔ 逆 时 针 \footnotesize 指向纸面外\Leftrightarrow叉积大于0\Leftrightarrow逆时针 指向纸面外⇔叉积大于0⇔逆时针
指 向 纸 面 里 ⇔ 叉 积 小 于 0 ⇔ 顺 时 针 \footnotesize 指向纸面里\Leftrightarrow叉积小于0\Leftrightarrow顺时针 指向纸面里⇔叉积小于0⇔顺时针
x ⃗ × y ⃗ > 0 : \vec{x}\times \vec{y}>0: x×y>0:
x ⃗ × y ⃗ < 0 : \vec{x}\times \vec{y}<0: x×y<0:
x ⃗ × y ⃗ = 0 : x ⃗ 和 y ⃗ 共 线 \vec{x}\times \vec{y}=0: \vec{x}和\vec{y} 共线 x×y=0:x和y共线
判断点与直线的位置关系
TOYS
遇到第一个L[i].s P × L[i].s L[i].s < 0 时,i - 1所在的格子玩具个数+1
#include
#include
#include
#include
#include
#pragma warning(disable:4996)
using namespace std;
int sgn(double x) {
if (fabs(x) < eps) return 0;
if (x < 0) return -1;
else return 1;
}
struct Point {
double x;
double y;
Point(double _x = 0, double _y = 0) {
x = _x;
y = _y;
}
Point operator - (Point b) {
return Point{
x - b.x, y - b.y };
}
};
struct Line {
Point e, s;
Line() {
}
Line(Point _e, Point _s) {
e = _e;
s = _s;
}
// 注意有两个 const
bool operator < (const Line &b) const {
return s.x < b.s.x;
}
};
const double eps = 1e-8;
const int N = 1e4 + 5;
Line L[N];
int ans[N];
double chaji(Point a, Point b, Point c)
{
Point x = b - a; // 向量ab
Point y = c - a; // 向量ac
return x.x * y.y - x.y * y.x;
}
int main()
{
int n, m;
double x1, y1, x2, y2;
while (scanf("%d", &n) && n) {
scanf("%d", &m);
scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
double u1, u2;
for (int i = 1; i <= n; ++i) {
scanf("%lf%lf", &u1, &u2);
L[i].e = Point{
u1, y1 };
L[i].s = Point{
u2, y2 };
}
memset(ans, 0, sizeof(ans));
L[0].e = Point{
x1, y1 };
L[0].s = Point{
x1, y2 };
L[n + 1].e = Point{
x2, y1 };
L[n + 1].s = Point{
x2, y2 };
Point p;
for (int i = 1; i <= m; ++i) {
scanf("%lf%lf", &p.x, &p.y);
for (int j = 0; j <= n + 1; ++j) {
if (sgn(chaji(L[j].s, p, L[j].e)) < 0) {
++ans[j - 1];
break;
}
}
}
for (int i = 0; i <= n; ++i)
printf("%d: %d\n", i, ans[i]);
printf("\n");
}
system("pause");
return 0;
}
跨立实验:
e − s → × v . s − s → \overrightarrow{e-s}\times\overrightarrow{v.s-s} e−s×v.s−s :大于0(小于0),右手定则指向纸面外(内),逆(顺)时针
e − s → × v . s − s → \overrightarrow{e-s}\times\overrightarrow{v.s-s} e−s×v.s−s :小于0(大于0),右手定则指向纸面内(外),顺(逆)时针
e − s → × v . s − s → \overrightarrow{e-s}\times\overrightarrow{v.s-s} e−s×v.s−s与 e − s → × v . e − s → \overrightarrow{e-s}\times\overrightarrow{v.e-s} e−s×v.e−s 异号,所以它们的叉积小于零:
e − s → × v . s − s → ⋅ e − s → × v . e − s → < 0 \overrightarrow{e-s}\times\overrightarrow{v.s-s} \cdot \overrightarrow{e-s}\times\overrightarrow{v.e-s} < 0 e−s×v.s−s⋅e−s×v.e−s<0
Segments
#include
#include
#include
#include
#include
#pragma warning(disable:4996)
using namespace std;
const double eps = 1e-8;
const int N = 105;
int sgn(double x) {
if (fabs(x) < eps) return 0;
if (x < 0) return -1;
else return 1;
}
struct Point {
double x, y;
Point() {
}
Point(double _x, double _y) {
x = _x;
y = _y;
}
double operator ^ (const Point &b) const {
return x * b.y - y * b.x;
}
Point operator - (const Point &b) const {
return Point(x - b.x, y - b.y);
}
double operator * (const Point &b) const {
return x * b.x + y * b.y;
}
bool operator == (Point b) const {
return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
}
};
struct Line {
Point s, e;
Line() {
}
Line(Point _s, Point _e) {
s = _s;
e = _e;
}
// 跨立实验,判断线段与直线的交点
int segcrossseg(Line v) {
int d1 = sgn((e - s) ^ (v.s - s));
int d2 = sgn((e - s) ^ (v.e - s));
if ((d1 ^ d2) == -2) return 2;
return (d1 == 0 || d2 == 0);
}
};
Point p[N << 1];
Line L[N];
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
int n, cnt = 0;
scanf("%d", &n);
double x1, y1, x2, y2;
for (int i = 1; i <= n; ++i) {
scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
p[++cnt] = Point{
x1, y1 };
p[++cnt] = Point{
x2, y2 };
L[i] = Line{
p[cnt - 1], p[cnt] };
}
bool flag = true;
for (int i = 1; i <= cnt; ++i) {
for (int j = i + 1; j <= cnt; ++j) {
if (!(p[i] == p[j])) {
flag = true;
for (int k = 1; k <= n; ++k) {
if (Line(p[i], p[j]).segcrossseg(L[k]) == 0) {
flag = false;
break;
}
}
if (flag) break;
else continue;
}
else continue;
if (flag) break;
}
if (flag) break;
}
if (flag) printf("Yes!\n");
else printf("No!\n");
}
system("pause");
return 0;
}
1)判断是否平行:叉积判断
2)平行判断是否重合:通过叉积判断一个直线上一点是否在另一个直线上
3)不平行且不重合求交点
Intersecting Lines
#include
#include
#include
#include
#include
#pragma warning(disable:4996)
using namespace std;
const double eps = 1e-10;
int sgn(double x) {
if (fabs(x) < eps) return 0;
if (x < 0) return -1;
else return 1;
}
struct Point {
double x, y;
Point() {
}
Point(double _x, double _y) {
x = _x;
y = _y;
}
double operator ^(const Point &b) const {
return x * b.y - y * b.x;
}
double operator *(const Point &b) const {
return x * b.x + y * b.y;
}
Point operator + (const Point &b) const {
return Point(x + b.x, y + b.y);
}
Point operator - (const Point &b) const {
return Point(x - b.x, y - b.y);
}
};
struct Line {
Point s, e;
Line() {
}
Line(Point _s, Point _e) {
s = _s;
e = _e;
}
// 点和直线的关系
int relation(Point p) {
int c = sgn((p - s) ^ (e - s));
if (c < 0) return 1;
else if (c > 0) return 2;
else return 3;
}
// 直线平行
bool parallel(Line v) {
return sgn((e - s) ^ (v.e - v.s)) == 0;
}
// 直线关系,0:平行,1:重合,2:相交
int linecrossline(Line v) {
if ((*this).parallel(v))
return v.relation(s) == 3;
return 2;
}
// 求两直线的交点
Point crosspoint(Line v) {
double a1 = (v.e - v.s) ^ (s - v.s);
double a2 = (v.e - v.s) ^ (e - v.s);
return Point((s.x * a2 - e.x * a1) / (a2 - a1), (s.y * a2 - e.y * a1) / (a2 - a1));
}
};
int main()
{
int T;
while (scanf("%d", &T) != EOF) {
printf("INTERSECTING LINES OUTPUT\n");
while (T--)
{
double x1, y1, x2, y2;
scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
Line L1(Point(x1, y1), Point(x2, y2));
scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
Line L2(Point(x1, y1), Point(x2, y2));
int n = L1.linecrossline(L2);
if (n == 0) printf("NONE\n");
else if (n == 1) printf("LINE\n");
else {
Point p = L1.crosspoint(L2);
printf("POINT %.2lf %.2lf\n", p.x, p.y);
}
}
printf("END OF OUTPUT\n");
}
//system("pause");
return 0;
}
两次跨立实验判断
The Doors
线段是否是规范相交判断+求最短路
建图:
算法
存储所有墙的端点(除了和周围墙的交点),存储所有墙的线段
选取任意两个不同的端点,判断和所有线段是否存在规范相交,如果不存在则建图,权值为两点之间的距离。
建完图之后跑最短路算法(Floyd或者Dijkstra算法)
#include
#include
#include
#include
#include
#include
#pragma warning(disable:4996)
using namespace std;
const double eps = 1e-8;
const int N = 1e3 + 5;
const double inf = 1e9;
int sgn(double x) {
if (fabs(x) < eps) return 0;
if (x < 0) return -1;
else return 1;
}
struct Point {
double x, y;
Point() {
}
Point(double _x, double _y) {
x = _x;
y = _y;
}
double operator * (const Point &b) const {
return x *b.x + y * b.y;
}
double operator ^ (const Point &b) const {
return x * b.y - y * b.x;
}
Point operator - (const Point &b) const {
return Point(x - b.x, y - b.y);
}
double distance(Point p) {
return hypot(x - p.x, y - p.y);
}
};
struct Line {
Point s, e;
Line() {
}
Line(Point _s, Point _e) {
s = _s;
e = _e;
}
// 线段相交判断
// 2 规范相交
// 1 非规范相交
// 0 不相交
int segcrossseg(Line v) {
int d1 = sgn((e - s) ^ (v.s - s));
int d2 = sgn((e - s) ^ (v.e - s));
int d3 = sgn((v.e - v.s) ^ (s - v.s));
int d4 = sgn((v.e - v.s) ^ (e - v.s));
if ((d1 ^d2) == -2 && (d3 ^ d4) == -2) return 2;
return (d1 == 0 && sgn((v.s - s)*(v.s - e)) <= 0) ||
(d2 == 0 && sgn((v.e - s) *(v.e - e)) <= 0) ||
(d3 == 0 && sgn((s - v.s) *(s - v.e)) <= 0) ||
(d4 == 0 && sgn((e - v.s) *(e - v.e)) <= 0);
}
};
Point p[N];
Line L[N];
double dis[N][N];
int main()
{
int n;
while (scanf("%d", &n) && n != -1) {
for (int i = 0; i <= 4 * n + 1; ++i)
for (int j = 0; j <= 4 * n + 1; ++j)
dis[i][j] = inf;
p[0] = Point(0, 5);
double x, d1, m1, m2, u1;
int lcnt = 0, pcnt = 0;
for (int i = 1; i <= n; ++i) {
scanf("%lf%lf%lf%lf%lf", &x, &d1, &m1, &m2, &u1);
L[++lcnt] = Line(Point(x, 0), Point(x, d1));
L[++lcnt] = Line(Point(x, m1), Point(x, m2));
L[++lcnt] = Line(Point(x, u1), Point(x, 10));
p[++pcnt] = Point(x, d1);
p[++pcnt] = Point(x, m1);
p[++pcnt] = Point(x, m2);
p[++pcnt] = Point(x, u1);
}
p[++pcnt] = Point(10, 5);
for (int i = 0; i <= pcnt; ++i) {
for (int j = 0; j <= pcnt; ++j) {
if (i != j) {
Line L1(p[i], p[j]);
bool ok = true;
for (int k = 1; k <= lcnt; ++k) {
if (L1.segcrossseg(L[k]) == 2) {
ok = false;
break;
}
}
if (ok) dis[i][j] = p[i].distance(p[j]);
}
}
}
for (int k = 0; k <= pcnt; ++k) {
for (int i = 0; i <= pcnt; ++i) {
for (int j = 0; j <= pcnt; ++j) {
if (dis[i][k] + dis[k][j] < dis[i][j]) {
dis[i][j] = dis[i][k] + dis[k][j];
}
}
}
}
printf("%.2f\n", dis[0][pcnt]);
}
return 0;
}
Pick-up sticks
寻找最上面的木棍编号。
一不小心就会TLE,需要先存储所有线段,然后判断当前线段有没有被之后的线段覆盖,如果覆盖就break,然后记录线段编号用于优化常数
#include
#include
#include
#include
#include
#include
#pragma warning(disable:4996)
using namespace std;
const double eps = 1e-8;
const int N = 1e5 + 5;
int sgn(double x) {
if (fabs(x) < eps) return 0;
if (x < 0) return -1;
else return 1;
}
struct Point {
double x, y;
Point() {
}
Point(double _x, double _y) {
x = _x;
y = _y;
}
Point operator - (const Point &b) const {
return Point(x - b.x, y - b.y);
}
double operator ^ (const Point &b) const {
return x * b.y - y * b.x;
}
double operator * (const Point &b) const {
return x * b.x + y * b.y;
}
};
struct Line {
Point s, e;
Line() {
}
Line(Point _s, Point _e) {
s = _s;
e = _e;
}
// 两线段相交判断
// 2规范相交
// 1非规范相交
// 0不相交
int segcrossseg(Line v) {
int d1 = sgn((e - s) ^ (v.s - s));
int d2 = sgn((e - s) ^ (v.e - s));
int d3 = sgn((v.e - v.s) ^ (s - v.s));
int d4 = sgn((v.e - v.s) ^ (e - v.s));
if ((d1 ^ d2) == -2 && (d3 ^ d4) == -2) return 2;
return (d1 == 0 && sgn((v.s - s)*(v.s - e)) <= 0) ||
(d2 == 0 && sgn((v.e - s)*(v.e - e)) <= 0) ||
(d3 == 0 && sgn((s - v.s)*(s - v.e)) <= 0) ||
(d4 == 0 && sgn((e - v.s)*(e - v.e)) <= 0);
}
};
Line L[N];
bool st[N];
int ans[N];
int main()
{
int n;
while (scanf("%d", &n) && n) {
memset(st, false, sizeof(st));
double x1, y1, x2, y2;
for (int i = 1; i <= n; ++i) {
scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
L[i] = Line(Point(x1, y1), Point(x2, y2));
}
int tot = 0;
for (int i = 1; i <= n; ++i) {
for (int j = i + 1; j <= n; ++j) {
if (L[i].segcrossseg(L[j])) {
st[i] = true;
break;
}
}
if (st[i] == false) ans[++tot] = i;
}
printf("Top sticks: ");
for (int i = 1; i < tot; ++i)
printf("%d, ", ans[i]);
printf("%d.\n", ans[tot]);
}
system("pause");
return 0;
}
确定极点后,利用叉积按照顺时针或逆时针的方向对点进行排序
Space Ant
蚂蚁的行动有三个限制:
1)不能右转
2)走过的路会留下红色的标记
3)不会经过之前走过的留下红色标记的路
#include
#include
#include
#include
#include
#include
#pragma warning(disable:4996)
using namespace std;
const double eps = 1e-8;
const int maxp = 55;
int sgn(double x) {
if (fabs(x) <= eps) return 0;
else if (x < 0) return -1;
else return 1;
}
struct Point {
double x, y;
int id;
Point() {
}
Point(double _x, double _y) {
x = _x;
y = _y;
}
bool operator < (Point b) const {
return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : y < b.y;
}
Point operator - (const Point &b) const {
return Point(x - b.x, y - b.y);
}
double operator ^(const Point &b) const {
return x * b.y - y * b.x;
}
double operator *(const Point &b) const {
return x * b.x + y * b.y;
}
double distance(Point p) {
return hypot(x - p.x, y - p.y);
}
};
Point p[maxp];
struct cmp {
Point p;
cmp(const Point &p0) {
p = p0; }
bool operator()(const Point &aa, const Point &bb) {
Point a = aa, b = bb;
int d = sgn((a - p) ^ (b - p));
if (d == 0) {
return sgn(a.distance(p) - b.distance(p)) < 0;
}
return d > 0;
}
};
int ans[maxp];
int main()
{
int T;
scanf("%d", &T);
while (T--) {
int n, id;
scanf("%d", &n);
double x, y;
for (int i = 0; i < n; ++i) {
scanf("%d%lf%lf", &id, &x, &y);
p[i] = Point(x, y);
p[i].id = id;
}
Point mi = p[0];
for (int i = 1; i < n; ++i) mi = min(mi, p[i]);
for (int i = 0; i < n; ++i) {
ans[i] = mi.id;
sort(p + i, p + n, cmp(mi));
mi = p[i + 1];
}
printf("%d", n);
for (int i = 0; i < n; ++i) printf(" %d", ans[i]);
printf("\n");
}
system("pause");
return 0;
}
写在最后的一点感想
可能这是最后一篇关于ACM算法的博客了、明天就是ICPC济南站了,500多个队伍只有200多个牌子。写一点感想吧,还记得大一完的暑假,因为是转专业的,之前从没学过程序设计,想着暑假提前学点东西,看到ACM有暑期训练就加入了,第一次的测试赛,我连一道求和的签到题都写不出来,但是暑期训练结束的比赛好歹可以写三道题了,还拿了一个最快解题奖。后来厚着脸皮加了ACM队,找了wq和yzl组队,不得不说我是真的菜,也很感谢队友不嫌弃。但是训练的经历对于课程的学习帮助很大,短时间内提高一个人的程序设计能力,自学能力,debug能力等等,对于一个人的影响很长远。
打ACM一年半了,第一年太菜了,只有这半年参加了CCPC一次,ICPC一次都打铁了,还有明天的最后一场退役赛,我们能拿牌吗?我不知道(或许我知道,但还是想有希望)。常常怀疑我是不是选错了路,ACM是不是不适合我,也许是沉没成本,也许是学习算法的新知识,也许是和队友一起学习训练的感觉,让我一直坚持到了现在。
最近一直在想没有了ACM我应该做些什么呢?大二和大三上的课余时间大多都用来刷题了,不用刷题之后,我有什么事情可以做呢。搞科研项目?做数学建模?准备考研?准备保研(又很悬)?学习英语?重拾吉他?我不知道,感觉自己一直在忙碌又没有成果,只有看得过去的可怜绩点和没有含金量的几个比赛奖项。