2 3
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
0,1
0,2,3
如果已知<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; }
研究了半天发现跟这个题并没有什么关系
我们不忙着解这道题。我们先来看一下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; }