目录
POJ2653 Pick-up sticks
POJ127 Jack Straws
POJ1066 Treasure Hunt
POJ1556 The Doors
POJ3449 Geometric Shapes
POJ1584 A Round Peg in a Ground Hole
题意:N根火柴按照顺序随意丢在桌面上,问哪些火柴没有被其他火柴压住。
思路:很简单的线段相交问题。只要火柴没有和后面丢下的火柴规范相交,则说明这根火柴没有被压住。
AC代码:
/*---------------------------------
*File name: B.cpp
*Creation date: 2020-07-15 09:08
*Link:
*-------------------------------*/
//#pragma GCC diagnostic error "-std=c++11"
//#include
#include
#include
#include
#include
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
const double eps = 1e-8;
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;
}
void input(){scanf("%lf %lf", &x, &y);}
};
struct Line{
Point s, e;
Line(){}
Line(Point _s,Point _e){
s = _s;
e = _e;
}
//判断线段相交,规范相交返回2
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[maxn];
int main(){
int n;
while(~scanf("%d", &n) && n){
for(int i = 1; i <= n; ++i){
double x1, x2, y1, y2;
scanf("%lf %lf %lf %lf", &x1, &y1, &x2, &y2);
l[i] = Line(Point(x1, y1), Point(x2, y2));
}
vector ans;
for(int i = 1; i <= n; ++i){
bool flag = 1;
for(int j = i + 1; j <= n; ++j){
//printf("%d corss : %d : %d\n", i, j, l[i].segcrossseg(l[j]));
if(l[i].segcrossseg(l[j]) == 0) continue;
flag = 0;
break;
}
if(flag) ans.pb(i);
}
printf("Top sticks: ");
for(int i = 0; i < ans.size() - 1; ++i) printf("%d, ", ans[i]);
printf("%d.\n", ans.back());
}
return 0;
}
题意:给出管道的坐标,当两个管道不规范相交时就算做连通,连通具有传递性。多次询问,针对每次询问输出询问的两根管道是否能够连通。
思路:并查集,给每个管道都赋予输入顺序作为编号。每次有新的管道与其余所有管道判断是否相交。相交则合并所在连通管道集合。比起上一题只是多了个并查集的内容。
AC代码:
/*---------------------------------
*File name: B.cpp
*Creation date: 2020-07-15 09:08
*Link:
*-------------------------------*/
//#pragma GCC diagnostic error "-std=c++11"
//#include
#include
#include
#include
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
const double eps = 1e-8;
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;
}
void input(){scanf("%lf %lf", &x, &y);}
};
struct Line{
Point s, e;
Line(){}
Line(Point _s,Point _e){
s = _s;
e = _e;
}
//返回1为不规范相交
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[maxn];
int pre[maxn];
int Find(int x){return x == pre[x] ? x : pre[x] = Find(pre[x]);}
inline void Union(int x, int y){
int px = Find(x);
int py = Find(y);
if(px == py)return;
pre[px] = py;
}
int main(){
int n;
while(~scanf("%d", &n) && n){
for(int i = 1; i <= n; ++i){
pre[i] = i;
double x1, x2, y1, y2;
scanf("%lf %lf %lf %lf", &x1, &y1, &x2, &y2);
l[i] = Line(Point(x1, y1), Point(x2, y2));
}
for(int i = 1; i < n; ++i){
for(int j = i + 1; j <= n; ++j){
if(l[i].segcrossseg(l[j]) != 0) Union(i, j);
}
}
int x, y;
while(~scanf("%d %d", &x, &y) && x && y){
if(Find(x) == Find(y)) printf("CONNECTED\n");
else printf("NOT CONNECTED\n");
}
}
return 0;
}
题意:有一个100*100的二维房子,房子被数道墙分割成了多个封闭的空间,其中一个空间保存着宝藏。现在要从房子外通过爆破墙壁的方法进入房子抵达宝藏所在的空间,问最少需要爆破几道墙就可以到达。
思路:墙壁都是直来直往的,不会拐弯,如果将墙左右两边的空间当作点,距离为1,那么到达宝藏所在空间的爆破数量就为距离。而相邻的两点之间必定是只有1道墙,所以直接判断所有墙的端点和宝藏所连线段与多少条线段相交就可以了。
AC代码:
/*---------------------------------
*File name: A.cpp
*Creation date: 2020-07-15 21:55
*Link:
*-------------------------------*/
//#pragma GCC diagnostic error "-std=c++11"
//#include
#include
#include
#include
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair
using namespace std;
const int maxn = 30 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
const double eps = 1e-8;
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;
}
void input(){scanf("%lf %lf", &x, &y);}
void output(){printf("%.2f %.2f\n", x, y);}
};
struct Line{
Point s,e;
Line(){}
Line(Point _s,Point _e){
s = _s;
e = _e;
}
void input(){
s.input();
e.input();
}
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[maxn];
int n;
int cal(Line cur){
int cnt = 0;
for(int i = 0; i < n; ++i){
if(cur.segcrossseg(l[i]) == 2) cnt++;
}
return cnt;
}
int main(){
scanf("%d", &n);
for(int i = 0; i < n; ++i) l[i].input();
Point ini;
ini.input();
int ans = inf;
for(int i = 0; i < n; ++i){
Line cur = Line(ini, l[i].e);
ans = min(ans, cal(cur));
cur = Line(ini, l[i].s);
ans = min(ans, cal(cur));
}
if(ans == inf) ans = 0;
printf("Number of doors = %d\n", ans + 1);
return 0;
}
以上三题主要为线段相交。
题意:有一个10*10的房间,有长短不一的墙竖直分布在房间中,要求从(0, 5)点移动至(10, 5)点,问最短路径是多少。
思路:最短路径肯定是最短路问题,最短路问题当然需要知道哪些点直接可以相互到达与之间的距离为多少。所以直接暴力枚举所有点与其他点所连线段是否与墙壁规范相交,若不相交,则说明两点之间可以相互到达。给点赋予输入顺序为编号。起点编号为0,终点编号为cntp+1。更新了所有点之间的位置关系之后随便用什么最短路算法处理就好。因为数据范围不大所以当然是写最简单的Floyd。
AC代码:
/*---------------------------------
*File name: A.cpp
*Creation date: 2020-07-15 21:55
*Link:
*-------------------------------*/
//#pragma GCC diagnostic error "-std=c++11"
//#include
#include
#include
#include
#include
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair
using namespace std;
const int maxn = 1e3 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
const double eps = 1e-8;
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;
}
double distance(Point p){
return hypot(x - p.x, y - p.y);
}
void input(double _x){x = _x, scanf("%lf", &y);}
void output(){printf("%.2f %.2f\n", x, y);}
}p[maxn];
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));
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[maxn];
int n;
double dis[maxn][maxn];
int cntl, cntp;
inline bool check(Line cur){
for(int i = 1; i <= cntl; ++i){
if(cur.segcrossseg(l[i]) == 2) return 0;
}
return 1;
}
inline double cal(Line cur){
return cur.e.distance(cur.s);
}
int main(){
while(~scanf("%d", &n) && n != -1){
cntl = cntp = 0;
for(int i = 0; i < n; ++i){
double x;
scanf("%lf", &x);
Point cur = Point(x, 0);
p[++cntp].input(x);
l[++cntl] = Line(cur, p[cntp]);
p[++cntp].input(x); cur = p[cntp];
p[++cntp].input(x);
l[++cntl] = Line(cur, p[cntp]); cur = Point(x, 10);
p[++cntp].input(x);
l[++cntl] = Line(p[cntp], cur);
}
p[0] = Point(0, 5), p[cntp+1] = Point(10, 5);
for(int i = 0; i <= cntp; ++i){
for(int j = 0; j < i; ++j) dis[i][j] = 100000;
dis[i][i] = 0;
for(int j = i + 1; j <= cntp + 1; ++j){
Line tmp(p[i], p[j]);
if(check(tmp)) {
dis[i][j] = cal(tmp);
dis[j][i] = dis[i][j];
}
else dis[i][j] = dis[j][i] = 100000;
}
}
for(int k = 0; k <= cntp + 1; ++k){
for(int i = 0; i <= cntp + 1; ++i){
for(int j = 0; j <= cntp + 1; ++j){
if(dis[i][j] > dis[i][k] + dis[k][j]) dis[i][j] = dis[i][k] + dis[k][j];
}
}
}
printf("%.2f\n", dis[0][cntp+1]);
}
return 0;
}
题意:给一些形状:多边形、正方形、矩形、三角形与线段。每个形状都有一个编号。问每一个形状与哪些形状相交或者不与任何一个形状相交。
思路:这题其实就是输入比较烦人。当输入不同的形状的时候可能还要输入不同数量的点,然后输入正方形和矩形的时候只要计算以下另外的点的坐标加入就好了。具体的我把代码拆分了贴上来吧。
拆分代码部分:
初始化部分:
mp["square"] = mp["line"] = 2;
mp["triangle"] = mp["rectangle"] = 3;
输入部分:
while(1){
int cnt = -1;
while(~scanf(" %c", &n) && n != '-' && n != '.'){
char k[10];
scanf(" %s", k);
int len;
if(strcmp("polygon", k) == 0) scanf("%d", &len);
else len = mp[k];
pol[++cnt].input(len);
if(strcmp("square", k) == 0){//输入的是正方形的对角坐标,列个方程组就可以得到另外两个点的坐标计算公式。
double a, b, c, d;
a = pol[cnt].p[0].x, b = pol[cnt].p[0].y;
c = pol[cnt].p[1].x, d = pol[cnt].p[1].y;
pol[cnt].add( Point((a-b+c+d)/2, (a+b-c+d)/2) );
pol[cnt].add( Point((a+b+c-d)/2, (-a+b+c+d)/2) );
}
else if(strcmp("rectangle", k) == 0){//输入的是矩形
pol[cnt].add( Point(pol[cnt].p[2] - pol[cnt].p[1] + pol[cnt].p[0]) );
}
strcpy(pol[cnt].kind, k);//kind为形状名称
pol[cnt].name = n;//name为形状编号名
pol[cnt].norm();//极角排序
pol[cnt].getline();
}
if(n == '-') solve(cnt);
else break;
}
Solve函数部分:
inline void solve(int cnt){
sort(pol, pol + cnt + 1, cmp);
vector ans[30];
//PRINT(cnt);
for(int i = 0; i <= cnt; ++i){
for(int j = i + 1; j <= cnt; ++j){
//printf("Pol %c and %c inter:\n", pol[i].name, pol[j].name);
if(Polinter(pol[i], pol[j])){//枚举所有形状并判断位置关系
ans[i].pb(pol[j].name);
ans[j].pb(pol[i].name);
}
}
//根据size判断与多少形状相交并根据要求输出
if(ans[i].size() == 0) printf("%c has no intersections\n", pol[i].name);
else if(ans[i].size() == 1) printf("%c intersects with %c\n", pol[i].name, ans[i].back());
else if(ans[i].size() == 2) printf("%c intersects with %c and %c\n", pol[i].name, ans[i].front(), ans[i].back());
else{
printf("%c intersects with ", pol[i].name);
for(int j = 0; j < ans[i].size() - 1; ++j) printf("%c, ", ans[i][j]);
printf("and %c\n", ans[i].back());
}
}
printf("\n");
}
其余就是板子部分了,直接贴在全部代码里。
AC代码:
/*---------------------------------
*File name: B.cpp
*Creation date: 2020-07-15 09:08
*Link:
*-------------------------------*/
//#pragma GCC diagnostic error "-std=c++11"
//#include
#include
题意:有一块板,板上有1个由N的点组成的多边形洞。现在要将一个半径为R的圆形钉子插在坐标为(X, Y)的地方。问。算了,题意是问多边形是否是凸包以及圆和凸包之间的关系。
思路:板子题,先判凸包,然后判圆和多边形的位置关系。
AC代码:
/*---------------------------------
*File name: A.cpp
*Creation date: 2020-07-17 09:02
*Link:
*-------------------------------*/
//#pragma GCC diagnostic error "-std=c++11"
//#include
#include
#include
#include
#include
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
const double eps = 1e-8;
int n;
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;
}
bool operator == (Point b)const{
return sgn(x-b.x) == 0 && sgn(y-b.y) == 0;
}
double distance(Point p){
return hypot(x - p.x, y - p.y);
}
void input(){scanf("%lf %lf", &x, &y);}
};
struct Line{
Point s,e;
Line(){}
Line(Point _s,Point _e){
s = _s;
e = _e;
}
double length(){
return s.distance(e);
}
bool pointonseg(Point p){
return sgn((p-s) ^ (e-s)) == 0 && sgn((p-s) * (p-e)) <= 0;
}
double dispointtoline(Point p){
return fabs((p-s)^(e-s))/length();
}
double dispointtoseg(Point p){
if(sgn((p-s)*(e-s))<0 || sgn((p-e)*(s-e))<0)
return min(p.distance(s),p.distance(e));
return dispointtoline(p);
}
};
struct circle{
Point p;
double r;
circle(){}
circle(Point _p,double _r){
p = _p;
r = _r;
}
int relationseg(Line v){
double dst = v.dispointtoseg(p);
if(sgn(dst-r) < 0)return 2;
else if(sgn(dst-r) == 0)return 1;
return 0;
}
}peg;
struct Polygon{
int n;
Point p[maxn];
Line l[maxn];
void input(int _n){
n = _n;
for(int i = 0;i < n;i++)
p[i].input();
}
void getline(){
for(int i = 0;i < n;i++){
l[i] = Line(p[i],p[(i+1)%n]);
}
}
int relationpoint(Point q){
for(int i = 0;i < n;i++){
if(p[i] == q)return 3;
}
getline();
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;
}
int relationcircle(circle c){
//` 2 圆完全在多边形内`
//` 1 圆在多边形里面,碰到了多边形边界`
//` 0 其它`
getline();
int x = 2;
if(relationpoint(c.p) != 1) return 0;//圆心不在内部
for(int i = 0;i < n;i++){
if(c.relationseg(l[i])==2)return 0;
if(c.relationseg(l[i])==1)x = 1;
}
return x;
}
//`判断是不是凸包`
bool isconvex(){
bool s[3];
memset(s,false,sizeof(s));
for(int i = 0;i < n;i++){
int j = (i+1)%n;
int k = (j+1)%n;
s[sgn((p[j]-p[i])^(p[k]-p[i]))+1] = true;
if(s[0] && s[2])return false;
}
return true;
}
}Hole;
void solve(){
if(!Hole.isconvex()){
printf("HOLE IS ILL-FORMED\n");
return;
}
if(Hole.relationcircle(peg) == 0){
printf("PEG WILL NOT FIT\n");
return;
}
printf("PEG WILL FIT\n");
}
int main(){
while(~scanf("%d", &n) && n >= 3){
Point mid;
double r;
scanf("%lf", &r);
mid.input();
peg = circle(mid, r);
Hole.input(n);
solve();
}
return 0;
}