目录
- 前言
- 题目
- 1.[TOYS POJ - 2318 ](解决)
- 2.[Toy Storage POJ - 2398 ](解决)
- 3.[Segments POJ - 3304 ](解决)
- 4.[Intersecting Lines POJ - 1269 ](解决)
- 5.[The Doors POJ - 1556 ](解决)
- 6.[Pick-up sticks POJ - 2653 ](解决)
- 7.[Treasure Hunt POJ - 1066](待消化)
- 8.[Intersection POJ - 1410 ](判断点是否在任意多边形内部)(解决)
- 9.[Space Ant POJ - 1696 ]
- 10.[Kadj Squares POJ - 3347 ]
- 11.[An Easy Problem?! POJ - 2826 ]
- 12.[Pipe POJ - 1039 ]
- 13.[Geometric Shapes POJ - 3449 ]
- 14.[A Round Peg in a Ground Hole POJ - 1584 ]
前言
参考博客
自己总结的东西:
难度判断?
题目
1.[TOYS POJ - 2318 ](解决)
- 题意:给定n+1个区间,m个点,问各区间各多少个点。
- n+1个区间排在一行,划分一个大的矩形,每个区间的上下底都平行。具体如下:
- n+1个区间排在一行,划分一个大的矩形,每个区间的上下底都平行。具体如下:
- 提示:二分
- 代码
2.[Toy Storage POJ - 2398 ](解决)
- 说明:和第1题一模一样,注意这题线没排序。
- 代码
3.[Segments POJ - 3304 ](解决)
- 题意:给定n(<=100)条线段,每条线段给两个端点。问是否有一条直线能让所有线段都与它有交点?
- 提示:如果存在这样的直线,那么这条直线一定可以通过这些线段的某两个端点(也可能是一条线段的两个端点)。即从结果出发。
- 从结果理解起来没那么难理解。
- 至于具体如何,自己画一下。
- 判断直线与线段是否相交:返回1,2为相交
// kuangbin模板+备注
//直线和线段相交判断
//-*this line -v seg
// 2 规范相交:相交,且交点不在端点
// 1 非规范相交:在端点处相交
// 0 不相交
// Line:::(v为线段)
int linecrossseg(Line v) {
int d1 = sgn((e−s) ^ (v.s−s));
int d2 = sgn((e−s) ^ (v.e−s));
if ((d1 ^ d2) ==−2) return 2; //-1^1=-2
return (d1 == 0 || d2 == 0);
}
- 代码:
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const int N = 2e2 + 10;
const double eps = 1e-8;
int sgn(double x) {
if (fabs(x) < eps) return 0;
return (x > 0) ? 1 : -1;
}
int n, m;
int a[N], b[N];
struct Point {
double x, y;
Point() {}
Point(double _x, double _y) { x = _x, y = _y; }
void input() { scanf("%lf%lf", &x, &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; }
bool operator==(const Point &b) const {
return (sgn(x - b.x) == 0) && (sgn(y - b.y) == 0);
}
} p[N];
double cross(Point a, Point b, Point c) { return (b - a) ^ (c - a); }
struct Line {
Point s, e;
Line() {}
Line(Point _s, Point _e) { s = _s, e = _e; }
};
//直线b,线段a
// kuangbin模板+备注
//直线和线段相交判断
//-*this line -v seg
// 2 规范相交:相交,且交点不在端点
// 1 非规范相交:在端点处相交
// 0 不相交
// Line:::(v为线段)
int linecrossseg(Line a, Line v) {
int d1 = sgn((a.e - a.s) ^ (v.s - a.s));
int d2 = sgn((a.e - a.s) ^ (v.e - a.s));
if ((d1 ^ d2) == -2) return 2; //-1^1=-2
return (d1 == 0 || d2 == 0);
}
bool solve() {
bool f = false;
int f1, f2;
for (int i = 1; i <= 2 * n; i++) {
for (int j = i; j <= 2 * n; j++) {
f = true;
if (p[i] == p[j]) continue;
for (int k = 1; k <= n; k++) {
if (!linecrossseg(Line(p[i], p[j]),
Line(p[2 * k - 1], p[2 * k])))
f = false;
}
if (f) return true;
}
}
return false;
}
signed main() {
int T;
cin >> T;
while (T--) {
cin >> n;
for (int i = 1; i <= 2 * n; i++) {
p[i].input();
}
puts(solve() ? "Yes!" : "No!");
}
return 0;
}
4.[Intersecting Lines POJ - 1269 ](解决)
- 题意:
- 代码:
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const double eps = 1e-8;
int sgn(double x) {
if (fabs(x) < eps) return 0;
if (x > 0)
return 1;
else
return -1;
}
int n;
struct Point {
double x, y;
Point(double x = 0, double y = 0) : x(x), y(y) {}
void input() { scanf("%lf%lf", &x, &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); }
double operator^(const Point &b) const { return x * b.y - b.x * y; }
Point operator*(double k) const { return Point(k * x, k * y); }
};
struct Line {
Point s, e;
Line() {}
Line(Point s, Point e) { s = s, e = e; }
void input() { s.input(), e.input(); }
//判断两线段是否相交(不考虑相交于端点)
bool sgecrossseg(Line b) {
double c1 = (e - s) ^ (b.s - s), c2 = (e - s) ^ (b.e - s);
double c3 = (b.e - b.s) ^ (s - b.s), c4 = (b.e - b.s) ^ (e - b.s);
return sgn(c1) * sgn(c2) < 0 && sgn(c3) * sgn(c4) < 0;
}
//求直线交点
Point linecrossline(Line b) {
// b.s点为基准,return s+t*v;
Point u = Point(e - s), v = Point(b.e - b.s), w = Point(e - b.s);
double t = (u ^ w) / (u ^ v);
// !t=fabs(t);//防止搞错,不对,不能这样,因为本来t可能为负数
// dbg(b.s.x);
// dbg(b.s.y);
// dbg(v.x);
// dbg(v.y);
// dbg(t);
return b.s + v * t;
}
} l1, l2;
signed main() {
cin >> n;
puts("INTERSECTING LINES OUTPUT");
while (n--) {
l1.input(), l2.input();
//平行
if (sgn((l1.e - l1.s) ^ (l2.e - l2.s)) == 0) {
//判断是否三点共线,重合
if (sgn(Point(l2.s - l1.s) ^ Point(l2.e - l1.s)) == 0)
puts("LINE");
else
puts("NONE");
} else {
Point ans = l1.linecrossline(l2);
printf("POINT %.2lf %.2lf\n", ans.x, ans.y);
}
}
puts("END OF OUTPUT");
return 0;
}
5.[The Doors POJ - 1556 ](解决)
- 题意:
1.1 起点为(0,5),终点为(10,5),中间有最多18个挡板(统一两个洞),求从起点到终点的最小距离,不能穿过挡板。 - 题解:从结果出发,路径一定是通过了若干个端点(包括起点,终点,挡板边缘的端点)。我们只需要建一个图,边为能直接到达的两个端点,最后跑最短路即可。
- 代码:
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <vector>
#define to first
#define w second
#define pid pair<int, double>
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const int N = 18 * 4 + 10;
const double eps = 1e-8;
const double inf = 1e9;
int sgn(double x) {
if (fabs(x) < eps) return 0;
return (x > 0) ? 1 : -1;
}
int n;
double x, y[5];
struct Point {
double x, y;
Point(double x = 0, double y = 0) : 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; }
} sp, tp;
struct node {
Point p[6];
int n;
} a[N];
struct Line {
Point s, e;
Line() {}
Line(Point _s, Point _e) { s = _s, e = _e; }
};
double dist(Point a, Point b) {
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
//线段与线段之间是否有交点
//两线段相交判断
// 2 规范相交
// 1 非规范相交
// 0 不相交
// Line:::
int segcrossseg(Line a, Line v) {
int d1 = sgn((a.e - a.s) ^ (v.s - a.s));
int d2 = sgn((a.e - a.s) ^ (v.e - a.s));
int d3 = sgn((v.e - v.s) ^ (a.s - v.s));
int d4 = sgn((v.e - v.s) ^ (a.e - v.s));
if ((d1 ^ d2) == -2 && (d3 ^ d4) == -2) return 2;
return (d1 == 0 && sgn((v.s - a.s) * (v.s - a.e)) <= 0) ||
(d2 == 0 && sgn((v.e - a.s) * (v.e - a.e)) <= 0) ||
(d3 == 0 && sgn((a.s - v.s) * (a.s - v.e)) <= 0) ||
(d4 == 0 && sgn((a.e - v.s) * (a.e - v.e)) <= 0);
}
int s, t;
int f(int i, int j) {
if (i == 0) return 0;
if (i == n + 1) return 4 * n + 1;
return (i - 1) * 4 + j;
}
vector<pid> g[N];
int vis[N];
double dis[N];
void spfa(int s) {
for (int i = s; i <= t; i++) dis[i] = inf, vis[i] = 0;
dis[s] = 0;
queue<int> q;
q.push(s), vis[s] = 1;
while (!q.empty()) {
int x = q.front();
q.pop(), vis[x] = 0; //毕竟一般也不会有自环
// for (auto i : g[x]) {
/* if
* (vis[i.to])continue;不能这样,这个算法在队列中不代表不能够优化!——应该放在下面!*/
for (int j = 0; j < g[x].size(); j++) {
pid i = g[x][j];
if (dis[x] + i.w < dis[i.to]) {
dis[i.to] = dis[x] + i.w;
if (!vis[i.to]) q.push(i.to), vis[i.to] = 1;
}
}
}
}
signed main() {
while (cin >> n) {
if (n == -1) break;
for (int i = 0; i <= 4 * n + 1; i++) g[i].clear();
for (int i = 1; i <= n; i++) {
cin >> x >> y[1] >> y[2] >> y[3] >> y[4];
a[i].p[1] = Point(x, y[1]);
a[i].p[2] = Point(x, y[2]);
a[i].p[3] = Point(x, y[3]);
a[i].p[4] = Point(x, y[4]);
a[i].n = 4;
a[i].p[0] = Point(x, 0.0);
a[i].p[5] = Point(x, 10.0);
}
a[0].p[1] = Point(0.0, 5.0), a[0].n = 1;
a[n + 1].p[1] = Point(10.0, 5.0), a[n + 1].n = 1;
//四个点。
s = 0, t = 4 * n + 1;
for (int i = 0; i <= n + 1; i++) {
for (int ii = 1; ii <= a[i].n; ii++) {
for (int j = i + 1; j <= n + 1; j++) {
for (int jj = 1; jj <= a[j].n; jj++) {
bool ff = true;
for (int k = i + 1; k < j; k++) {
if (segcrossseg(Line(a[i].p[ii], a[j].p[jj]),
Line(a[k].p[0], a[k].p[1])) ||
segcrossseg(Line(a[i].p[ii], a[j].p[jj]),
Line(a[k].p[2], a[k].p[3])) ||
segcrossseg(Line(a[i].p[ii], a[j].p[jj]),
Line(a[k].p[4], a[k].p[5])))
ff = false;
if (!ff) break;
}
if (ff) {
// cout << i << " " << ii << " " << j << " " << j
// << endl;
// dbg(f(i, ii));
// dbg(f(j, jj));
g[f(i, ii)].push_back(make_pair(
f(j, jj), dist(a[i].p[ii], a[j].p[jj])));
}
}
}
}
}
// for (int i = s; i <= t; i++) {
// dbg(i);
// for (auto j : g[i]) {
// cout << j.first << " ";
// }
// cout << endl;
// }
spfa(s);
printf("%.2lf\n", dis[t]);
}
return 0;
}
6.[Pick-up sticks POJ - 2653 ](解决)
- 题意:扔n(<=100000)个棍子,按扔的顺序输入两个端点坐标,要求按输入的顺序输出顶部(上面没有任何棍子)的所有棍子(输出棍子是第多少次扔的)。
- “顶部”的棍子不超过1000个
- 题解:
- 相交不考虑端点。
- 代码(不重要的题目代码以这样的形式展示)
7.[Treasure Hunt POJ - 1066](待消化)
- 题意:
1.1 不超过30条线,给出线的两端点,求最少爆破多少道门,能找到宝藏(一个点)
1.2 爆破只能爆破门的中间(如上图) - 题解:
2.1 又需要那个技巧:枚举端点即可。可是这里不会用了emmm
2.2 参考Treasure Hunt POJ - 1066
2.3 终究是把题想得太难了emm(但是感觉没错?)。我的思路:求出所有中点,然后bfs一下,每次的标准是判断与所有线段的交点的个数。 - 代码:
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <queue>
#include <vector>
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const int N = 30 * 30 + 10;
const double eps = 1e-8;
const int inf = 1e9;
int sgn(double x) {
if (fabs(x) < eps) return 0;
return (x > 0) ? 1 : -1;
}
int n, cnt, cntp;
struct Point {
double x, y;
Point(double x = 0, double y = 0) : x(x), y(y) {}
void input() { cin >> x >> 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; }
} s, p[N];
double cross(Point a, Point b, Point c) { return (b - a) ^ (c - a); }
struct Line {
Point s, e;
Line() {}
Line(Point _s, Point _e) { s = _s, e = _e; }
void input() { s.input(), e.input(); }
// kuangbin模板
//两线段相交判断
// 2 规范相交
// 1 非规范相交
// 0 不相交
// Line:::
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);
}
} l[N];
int count(Line a) {
int res = 0;
for (int i = 1; i <= n; i++) {
int x = a.segcrossseg(l[i]);
if (x == 2) res++; //规范相交
}
return res;
}
signed main() {
cin >> n;
for (int i = 1; i <= n; i++)
l[i].input(), p[++cntp] = l[i].s, p[++cntp] = l[i].e;
p[++cntp] = Point(0, 0);
p[++cntp] = Point(100, 0);
p[++cntp] = Point(100, 100);
p[++cntp] = Point(0, 100);
s.input();
int ans = inf;
for (int i = 1; i <= cntp; i++) {
ans = min(ans, count(Line(s, p[i])));
}
ans++;
printf("Number of doors = %d\n", ans);
return 0;
}
8.[Intersection POJ - 1410 ](判断点是否在任意多边形内部)(解决)
- 题意:
1.1 给一条线段和一个实心矩形,问线段与矩形是否有交点。 - 题解:
2.1 无非两种情况:线段与边有交点,线段在矩形内。
2.2 判断线段是否在矩形内怎么操作?判断两个点是否都在里面 - 代码:
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
const int N = 5;
const double eps = 1e-8;
int sgn(double x) {
if (fabs(x) < eps) return 0;
return (x > 0) ? 1 : -1;
}
struct Point {
double x, y;
Point(double x = 0, double y = 0) : x(x), y(y) {}
void input() { cin >> x >> 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; }
bool operator==(const Point &b) const {
return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
}
} a[4];
double cross(Point a, Point b, Point c) { return (b - a) ^ (c - a); }
struct Line {
Point s, e;
Line() {}
Line(Point _s, Point _e) { s = _s, e = _e; }
void input() { s.input(), e.input(); }
// kuangbin模板+注解
// 点在线段上的判断
// Line:::
bool pointonseg(Point p) {
return sgn((p - s) ^ (e - s)) == 0 && sgn((p - s) * (p - e)) <= 0;
}
} l0;
struct polygon {
int n;
Point p[N];
Line l[N];
void getline() {
for (int i = 0; i < n; i++) l[i] = Line(p[i], p[(i + 1) % n]);
}
// kuangbin模板+注解
//判断点和任意多边形的关系
// 3 点上
// 2 边上
// 1 内部
// 0 外部
// polygon:::
int relationpoint(Point q) {
for (int i = 0; i < n; i++) {
if (p[i] == q) return 3;
}
getline(); //即得到多边形的n条边
for (int i = 0; i < n; i++) {
if (l[i].pointonseg(q)) return 2;
}
int cnt = 0;
for (int i = 0; i < n; i++) {
int j = (i + 1) % n;
int k = sgn((q - p[j]) ^ (p[i] - p[j]));
int u = sgn(p[i].y - q.y);
int v = sgn(p[j].y - q.y);
if (k > 0 && u < 0 && v >= 0) cnt++;
if (k < 0 && v < 0 && u >= 0) cnt--;
}
return cnt != 0;
}
} b;
//两线段相交判断
// 2 规范相交
// 1 非规范相交
// 0 不相交
// Line:::
int segcrossseg(Line a, Line v) {
int d1 = sgn((a.e - a.s) ^ (v.s - a.s));
int d2 = sgn((a.e - a.s) ^ (v.e - a.s));
int d3 = sgn((v.e - v.s) ^ (a.s - v.s));
int d4 = sgn((v.e - v.s) ^ (a.e - v.s));
if ((d1 ^ d2) == -2 && (d3 ^ d4) == -2) return 2;
return (d1 == 0 && sgn((v.s - a.s) * (v.s - a.e)) <= 0) ||
(d2 == 0 && sgn((v.e - a.s) * (v.e - a.e)) <= 0) ||
(d3 == 0 && sgn((a.s - v.s) * (a.s - v.e)) <= 0) ||
(d4 == 0 && sgn((a.e - v.s) * (a.e - v.e)) <= 0);
}
signed main() {
int T;
cin >> T;
while (T--) {
l0.input();
a[0].input(), a[2].input();
a[1] = Point(a[0].x, a[2].y);
a[3] = Point(a[2].x, a[0].y);
bool f = false; // true:有相交部分
for (int i = 0; i <= 3; i++) {
if (segcrossseg(Line(a[i], a[(i + 1) % 4]), l0)) f = true;
}
/*
//两个点都在里面的情况
if (!f){
// bool ff = true;
// for (int i = 0; i <= 3; i++) {
// if (sgn(cross(l0.s, a[i], a[(i + 1) % 4])) < 0) ff = false;
// if (sgn(cross(l0.e, a[i], a[(i + 1) % 4])) < 0) ff = false;
// }
// if (ff) f = ff;
} */
b.n = 4;
for (int i = 0; i < 4; i++) b.p[i] = a[i];
if (b.relationpoint(l0.s) && b.relationpoint(l0.e)) f = true;
puts(f ? "T" : "F");
}
return 0;
}
9.[Space Ant POJ - 1696 ]
- 题意:给定n(n<=50)个点,每次行走不能向右转(并且路径不能相交),问最多能走多少个点。
- 题解:
2.1 提示:极角排序
2.2 具体操作:先找出y最小的点(y相等找x小)(这里保证不相交),从这个点开始,加入栈,然后不断找以栈顶点为极坐标原点的极角最小的点(极角相等就找距离最近的点)。(这里保证不向右) - 代码:
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const int N = 50 + 10;
const double eps = 1e-8;
const int inf = 1e9;
int sgn(double x) {
if (fabs(x) < eps) return 0;
return (x > 0) ? 1 : -1;
}
int n, x, vis[N], cnt;
vector<int> ans;
struct Point {
double x, y;
Point(double x = 0, double y = 0) : x(x), y(y) {}
void input() { cin >> x >> 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 operator<(const Point &b) const {
if (sgn(y - b.y) == 0) return sgn(x - b.x) < 0;
return sgn(y - b.y) < 0;
}
} p[N], stk[N];
double cross(Point a, Point b, Point c) { return (b - a) ^ (c - a); }
double dis(Point a, Point b) {
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
void init() {
memset(vis, 0, sizeof(vis));
cnt = 0, ans.clear();
}
signed main() {
int T;
cin >> T;
while (T--) {
init();
cin >> n;
for (int i = 0; i < n; i++) cin >> x, p[i].input();
Point p0 = p[0];
int pos = 0;
for (int i = 1; i < n; i++) {
if (p[i] < p0) pos = i, p0 = p[i];
}
// p0 = p[0], pos = 0;
//如果不从y坐标最小点(y相等时x小在前)开始的话就可能会有交叉
ans.push_back(pos), vis[pos] = 1;
stk[++cnt] = p0;
// cnt=n的时候完成操作
while (cnt < n) {
for (int i = 0; i < n; i++) {
if (vis[i]) continue;
p0 = p[i], pos = i;
break;
}
for (int i = 0; i < n; i++) {
if (vis[i]) continue;
if (sgn(cross(stk[cnt], p0, p[i])) == 0 &&
sgn(dis(stk[cnt], p0) - dis(stk[cnt], p[i])) > 0)
p0 = p[i], pos = i;
else if (sgn(cross(stk[cnt], p0, p[i])) < 0)
p0 = p[i], pos = i;
}
ans.push_back(pos), vis[pos] = 1;
stk[++cnt] = p0;
}
cout << ans.size() << " ";
for (int i = 0; i < ans.size(); i++) cout << ans[i] + 1 << " ";
cout << endl;
}
return 0;
}
10.[Kadj Squares POJ - 3347 ]
- 题意:
1.1 给定n(1<=n<=50)个正方形,旋转45度,从前到后依次尽量靠左其不超出0,问能看到的正方形编号(从小到大打印)
1.2 每个正方形的边长给定。 - 题解:
2.1 可参考:POJ 3347 Kadj Squares(复杂的线段相交问题)
11.[An Easy Problem?! POJ - 2826 ]
12.[Pipe POJ - 1039 ]
13.[Geometric Shapes POJ - 3449 ]
14.[A Round Peg in a Ground Hole POJ - 1584 ]
A Round Peg in a Ground Hole POJ - 1584
- 题意: