Aizu 1379 Parallel Lines

博客内容讲述了作者在解决Aizu在线编程挑战中的1379题时遇到的问题,即如何找出最大数量的斜率相同的直线对,而不重复使用点。作者最初尝试预处理斜率,但发现不能重复使用点,于是转向暴力判断。通过计算复杂度,作者发现了错误的解决方案并修正为正确的双阶乘数列。此外,还介绍了如何将两重循环的dfs简化为一重循环,显著提高了算法效率。

题目链接:

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=1379
https://vjudge.net/contest/270706#problem/B

题意:给了N个点,问能最多能选出几对直线的斜率相同,斜率可能有多种,点不能重复使用

比赛的时候想把斜率预处理出来,然后枚举能组成这个斜率的直线有多少个
但是后来发现点不能重复使用。。。而这种方法弄出来的点的重复使用了的,所以就不行

然后看题解说直接暴力判断,我还想说暴力力题啊,不做了不做了,但是仔细想想暴力要怎么暴力喃?好像我还不怎么会暴力T_T,还是做一哈算了,结果。。。幸好做了,感觉这道题对我来说其实很有价值~

(一)计算复杂度

首先这个算复杂度的时候我傻逼了,我写了个dfs发现诶怎么符合条件的方案怎么那么多???
N=4:6种
N=6:90种
N=8:2520种

我觉得不应该是这么多啊,N=4的时候都只有3种呀
(1,2)(3,4)
(1,3)(2,4)
(1,4)(2,3)
N=4应该只有这三种
应该是有重复的

然后专门写了个暴力来跑,用的排列来暴力,好像算到12就算不动了T_T
N=4:3种
N=6:15种
N=8:105种

然后用前几项去OEIS查,发现竟然是个双阶乘的数列,双阶乘我还以为是阶乘的阶乘勒,结果要么是偶数的阶乘,要么是奇数的阶乘。

后来我反应过来了,上面N=8的时候有重复的是2520种,无重复的是105种是怎么来的了

每次选两种:
2520 = C 8 2 ⋅ C 6 2 ⋅ C 4 2 ⋅ C 2 2 2520=C_8^2\cdot C_6^2\cdot C_4^2\cdot C_2^2 2520=C82C62C42C22

然后8个点有4对,这4对排列是重复的
因此:
105 = C 8 2 ⋅ C 6 2 ⋅ C 4 2 ⋅ C 2 2 4 ! 105=\frac{C_8^2\cdot C_6^2\cdot C_4^2\cdot C_2^2}{4!} 105=4!C82C62C42C22

然后验证其他的,发现是对的

(二)dfs两重循换变一重

解决了上面的问题我又写了一发,跑最后一个样例的时候发现用了12000ms+
看别人的代码我惊讶地发现为什么别人的dfs里面只有一层循环呀???一次选两个数让我想怎么也要写两个for循环才行呀~
哇,原来别人是这样的:
首先看当前这个数能不能用
①:要是阔以用,那就用for循环再找另一个阔以用的数
②:如果不能用,就直接进入下一层dfs
哇,这样时间上测出来就直接少了4000ms+

最后统计答案那里,我开始是用map统计这种斜率的直线的个数来计算,改成两层for循环直接暴力,速度瞬间变成了1000ms+了,快了8倍左右。。。

#include"bits/stdc++.h"
#define out(x) cout<<#x<<"="<<x
#define C(n,m) (m>n?0:(long long)fac[(n)]*invf[(m)]%MOD*invf[(n)-(m)]%MOD)
using namespace std;
typedef long long LL;
const int maxn=1e2+5;
const int MOD=1e9+7;
int FULL,Max;
int a[maxn],t;
struct Point
{
	int x,y;
	Point() {}
	Point(int x,int y):x(x),y(y) {}
};
Point P[maxn];
pair<int,int>Slope[maxn][maxn];//怕用double炸精度啥的,所以用pair来表示斜率
int N;
int f()
{
	int res=0;
//	map<pair<int,int>,int>Mp;
//	for(int i=1; i<N; i+=2)Mp[Slope[a[i]][a[i+1]]]++;
//	for(auto i:Mp)
//	{
//		int n=i.second;
//		if(n>=2)res+=n*(n-1)/2;
//	}

	//for循环快8倍 
	for(int i=1; i<=N; i+=2)
	{
		for(int j=i+2; j<=N; j+=2)
		{
			int dx=Slope[a[i]][a[i+1]].first;
			int dy=Slope[a[i]][a[i+1]].second;
			int tx=Slope[a[j]][a[j+1]].first;
			int ty=Slope[a[j]][a[j+1]].second;
			if(dx==tx&&dy==ty)res++;
		}
	}
	return res;
}
int cnt;
void dfs(int STA,int now)
{
	cnt++;
	if(STA==FULL)
	{
		Max=max(Max,f());
		return ;
	}
	if(STA&(1<<now))dfs(STA,now+1);//如果当前这个不能选,直接进入下一个dfs
	else
	{
		for(int i=1; i<=N; i++)
		{
			if(now==i)continue;
			if(STA&(1<<i))continue;
			int sta=STA|(1<<i);
			sta|=(1<<now);
			a[++t]=now,a[++t]=i;//同时选now和i
			dfs(sta,now+1);
			t-=2;
		}
	}
}
clock_t t1,t2;
int main()
{
	while(cin>>N)
	{
		t=Max=FULL=0;
		for(int i=1; i<=N; i++)
		{
			cin>>P[i].x>>P[i].y;
			FULL|=(1<<i);
		}
		for(int i=1; i<=N; i++)//预处理出斜率
		{
			for(int j=i+1; j<=N; j++)
			{
				int x=P[i].x-P[j].x;
				int y=P[i].y-P[j].y;
				if(x==0)y=1;
				else if(y==0)x=1;
				else
				{
					int d=__gcd(x,y);
					x/=d,y/=d;
				}
				Slope[i][j]=Slope[j][i]=make_pair(x,y);

			}
		}
		t1=clock();
		cnt=0;
		dfs(0,1);
//		cout<<"cnt="<<cnt<<endl;
		t2=clock();
//		cout<<"time="<<t2-t1<<endl;
		cout<<Max<<endl;

	}
}

/*
16
327 449
-509 761
-553 515
360 948
147 877
-694 468
241 320
463 -753
-206 -991
473 -738
-156 -916
-215 54
-112 -476
-452 780
-18 -335
-146 77
*/

评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值