HDU ACM:1446 计算直线的交点数

Problem Description
平面上有n条直线,且无三线共点,问这些直线能有多少种不同交点数。
比如,如果n=2,则可能的交点数量为0(平行)或者1(不平行)。
 

Input
输入数据包含多个测试实例,每个测试实例占一行,每行包含一个正整数n(n<=20),n表示直线的数量.
 

Output
每个测试实例对应一行输出,从小到大列出所有相交方案,其中每个数为可能的交点数,每行的整数之间用一个空格隔开。
 

Sample Input
  
  
2 3
 

Sample Output
  
  
0 1 0 2 3
 

Author
lcy


问题分析:

1条线:0
2条线:0 1
3条线:0 2 3
4条线:0 3 4 5 6 
……

可以把直线分成两组,第一组是相互平行的直线,第二组是自由直线,在本组中可以相互平行、相交,但是不能与第一组相平行,否则分组就没有意义了。

设直线总是为N,设第一组有i条直线,则第二组N-i有条直线。
以4条线来分析:
1、当i=4,N-i=0时,交点数:0
2、当i=3,N-i=1时,交点数:3条平行线与1条自由线的交点个数+1条自由线能形成的交点个数,即3+0=3
3、当i=2,N-i=2时,交点数:2*2+{0,1}={4,5}
4、当i=1,N-i=3时,交点数:1*3+{0,2,3}={3,5,6}

由此分析:交点数=i*(N-i)+j条自由直线的交点数

实现:

起初想用直接一点的办法,用二维数组存储每种N下,可能的交点数。这方法有很多问题,一是会计算出很多重复的可能交点数,二是这些重复的情况会再次出现在后面的调用中,导致可能性非常大,数组难以存储。

后来的办法:n条直线的最多可能交点数为n*(n-1)/2,即前n个自然数的和。20条直线最多有190个交点。用一个二维数组DP[21][200],数组中只保存0、1,用来表示该下标对应的可能交点数是否有效。

例如:DP[3][2]=1,表示总数N为3条直线的情况下,2个交点的情况有效。

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int DP[21][200];
	int N,n,free,j;
	memset(DP,0,sizeof(DP));
	for(n=1;n<21;n++)
	{
		DP[n][0]=1;
	}
	for(n=1;n<21;n++)             			  	//直线总数
	{
		for(free=0;free<n;free++)  	 			//自由直线总数
		{
			for(j=0;j<n*(n-1)/2;j++)            //可能的交点数
			{
                if(DP[free][j]==1)              //若free条自由直线有j个交点的可能性为真
				{
					DP[n][free*(n-free)+j]=1;   //那么n条直线在free条自由直线,n-free条平行直线的情况下
												//的free*(n-free)+j个交点的可能性为真
				}
			}

		}
	}
	while(scanf("%d",&N)==1)
	{
		int i,top=N*(N-1)/2;
		for(i=0;i<=top;i++)
		{
			if(i==0)//为HDU ACM格式化输出
			{
				printf("0");
				continue;
			}
            if(DP[N][i]==1)
			{
				printf(" %d",i);
			}
		}
  		printf("\n");
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值