hdu1466计算直线的交点数&2050 折线分割平面【DP】

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

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

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

Sample Input
   
   
   
   
2 3
 

Sample Output
   
   
   
   
0 1 0 2 3
 

前一个题我想了两天啊啊啊 队友给了提示也没做出来啊T^T

而且一直陷在复杂度是O(n*n)的惯性思维里 殊不知O(N^3)的复杂度预处理也可以0ms输出 坑爹啊

陷入的第二个怪圈是偏执的认为 dp[n][~]一定是由dp[n-1][~]推导而来的 (⊙﹏⊙)b 状态转移不一定就要严格的遵守两个状态挨着啊 

思路:来自hdu ppt

n条直线互不平行且无三线共点的最多交点数max=1+2+……(n-1)=n(n-1)/2,

所以n=20的话,最大的交点数是190

本题是求有多少种交点数:

容易列举出N=1,2,3的情况:

0

01

023

如果已知<N的情况,我们来分析加入第N条直线的情况(这里N=4)

1、第四条与其余直线全部平行 => 无交点;

2、第四条与其中两条平行,交点数为(n-1)*1+0=3;

3、第四条与其中一条平行,这两条平行直线和另外两点直线的交点数为(n-2)*2=4,而另外两条直线既可能平行也可能相交,因此可能交点数为:

                   (n-2*2+0=4    或者         (n-2)*2+1=5     

4、 第四条直线不与任何一条直线平行,交点数为:

      (n-3)*3+0=3   或者 (n-3)*3+2=5    或者 (n-3)*3+3=6

n=4时,有0个,3个,4个,5个,6个不同交点数。

从上述n=4的分析过程中,我们发现:

m条直线的交点方案数

=m-r)条平行线与r条直线交叉的交点数

  + r条直线本身的交点方案

=m-r*r+r条之间本身的交点方案数(1<=r<=m

以上分析摘自ppt

可以得出状态转移方程(m-r*r+r条之间本身的交点方案数(1<=r<=m

dp需保存已解决的子问题的答案,在需要时再找出已求的答案。一般步骤:

1.找出最优解特征,并刻画出结构特征

2.递归定义出其最优值

3.以自底向上的方式计算出其最优值

4.根据计算最优值得到的信息,构造最优解。

如本题,可设个2维数组dp[21][192]来记录结点情况。当r条直线有j个结点时,

dp[i][(m-r)*r+j]可以表示i条直线情况。

p.s.自己推导时还自以为是的觉得从n=5以后 交点依次加2呢 →_→ 查数都能差错

#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int dp[30][1000],n;
int main()
{
    memset(dp,0,sizeof(dp));
    for(int i=0;i<21;i++)
        for(int j=0;j<192;j++)
        {
            if(j==0) dp[i][j]=1;
            else dp[i][j]=0;
        }
    dp[1][0]=1;
    dp[2][0]=1,dp[2][1]=1;
  //  dp[3][0]=1,dp[3][2]=1,dp[3][3]=1;
    for(int m=3;m<21;m++)
    {
        for(int r=0;r<m;r++)
        {
            for(int i=0;i<=192;i++)
            {
                if(dp[r][i])
                {
                    dp[m][i+(m-r)*r]=1;//之前这里居然能写成dp[m][dp[r][i]+(m-r)*r]你长不长心?
                }
            }
        }
    }
    while(~scanf("%d",&n))
    {
        printf("0");
        for(int i=1;i<=200;i++)
            if(dp[n][i])
                printf(" %d",i);
        printf("\n");
    }
    return 0;
}

再说这个其实自己没做过的 由于是在2000-2099中间 还是挺轻视的 ╭(╯^╰)╮

研究了半天发现跟这个题并没有什么关系

我们不忙着解这道题。我们先来看一下N条相交的直线最多能把平面分割成几块。

很明显,当添加第n条直线时,为了使平面最多,则第n条直线要与前面n-1条直线都相交,切没有任何三条线交于一个点。
这样,第n条直线一共有n-1个交点。我们知道,增加n个焦点,则增加n+1个平面。
所以n条直线分割平面最大数是1 + 1 + 2 + 3 + ... + n = (n2 + n + 2) / 2

熟悉了线分割平面,现在,我们再来看看,每次增加的不是一条直线,而是两条相互平行的线,那又如何呢?

当第N次添加时,前面已经有2N-2条直线了,按我们上面讨论的知道,第N次添加时,第2N-1条直线和第2N条直线各能增加2(n-1)+1个平面。
所以第N次添加增加的面数是2[2(n-1) + 1] = 4n - 2 个。因此,总面数应该是1 + 4n(n+1)/2 - 2n = 2n2 + 1

现在我们再来看如果把每次加进来的平行边让它们一头相交,情况又如何呢?

我们看到,平面1、3已经合为一个面,既少了一个面。因此,每当一组平行线相交后,就会减少一个面。
因此,本题所要求的折线分割平面,自然就是上面求的的平行线分割平面数减去N。
即2n2 - n + 1 

#include <stdio.h>

int main(void)
{
    int n, i;
    scanf("%d", &i);
    while (i-- && scanf("%d", &n))
        printf("%d\n", 2*n*n-n+1);

    return 0;
}

太过分了 居然只有这么短

你可能感兴趣的:(dp,HDU)