P1153 点和线

题目描述

平面上有一些点,你可以用直线将两点连接起来。那么有多少种方法可以把这些点连续地连起来,使得任何两个线都不交叉。

显然,三个点只有一种方法。四个点最多只有 3 种方法。写一个程序计算方法总数。

输入格式

每一行是一个点的坐标,坐标值是整数,中间用一个空格隔开。最后一个坐标是原点。任意三点不在一条直线上。

输出格式

输出方案总数。

输入输出样例

输入 #1复制

100 -10
-200 0
45 7
0 0

输出 #1复制

3

说明/提示

最多只有 10 个点。

  • 必须从一个点出发,途径所有点回到起点的路径才会被统计。

  • 两个方案不相同当且仅当围成的简单多边形不同。

暴搜出奇迹


不要去想什么高深的算法了,也不要去推奇奇怪怪的数学公式,先想想计算机的发明是为了什么吧。

解:

本题的搜索方式不难想到,即每次枚举一点与当前点连线,然后判断这条是否与已连的线相交。所以本题的唯一难点是判断线段相交。

这里需要运用向量叉积。 <baidu>

简单来说:两个向量a和b的向量积是一个向量,记作a×b,其模等于由a和b作成的平行四边形的面积,方向与平行四边形所在平面平面垂直,当站在这个方向观察时,a逆时针转过一个小于π的角到达b的方向。这个方向也可以用物理上的右手螺旋定则判断:右手四指弯向由a转到b的方向(转过的角小于π),拇指指向的就是向量积的方向:从被乘数抓向乘数。如下图:

设向量P=(x1,y1),Q=(x2,y2),则向量a与向量b的叉积仍是一个向量,也可把叉积定义为一个矩阵行列式:

这时,结合定义,可得:

①P×Q>0;则P在Q的顺时针方向;

②P×Q<0;则P在Q的逆时针方向;

③P×Q=0;则P与Q共线,但可能同向也可能反向;

对于本题

两条线段AB、CD,相交的充要条件是:A、B在直线CD的异侧且C、D在直线AB的异侧。也就是说从AC到AD的方向与从BC到BD的方向不同,从CA到CB的方向与从DA到DB的方向也不同。这样就可以通过矢量的叉积来判断两线段是否相交:(AC×AD)∗(BC×BD)<0 且 (CA×CB)∗(DA×DB)<0 (具体实现见代码AddCross函数)


Talk is cheap, show me the code

#include<bits/stdc++.h>
#define re register
#define mn 15
using namespace std;

int n=1;
int ans;

struct dr{
	double x,y;
} q[mn];

int v[mn]; //保存已选路径
bool vis[mn];

inline double Cross(dr a,dr b,dr c){
	return (a.x-c.x)*(b.y-c.y)-(a.y-c.y)*(b.x-c.x);
}

inline bool AC(dr a,dr b,dr c,dr d){
	return (Cross(c,d,a)*Cross(c,d,b)<0&&Cross(a,b,c)*Cross(a,b,d)<0);
}

inline bool Judge(int num,int b){
	for(re int i=2;i<num-1;i++){
		if(AC(q[v[i-1]],q[v[i]],q[v[num-1]],q[b])) return false;
	}
	return true;
}

void Dfs(int num){
	if(num==n+1){
		if(Judge(num,1)) ans++;
		return;
	}
	for(re int i=2;i<=n;i++){
		if(!vis[i]&&Judge(num,i)){
			vis[i]=1;
			v[num]=i;
			Dfs(num+1);
			vis[i]=0;
		}
	}
}

int main(){
	while(true){
		scanf("%lf %lf",&q[n].x,&q[n].y);
		if(q[n].x||q[n].y) n++;
		else break;
	}
	v[1]=1;
	Dfs(2);
	printf("%d",ans/2); //正序和倒序算一种情况
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

芙宁娜的狗是我

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值