有空练了练动态规划,用来练习的题目是航电的1466题,链接如下
这道题目注意是求交点的方案数,也就是说,给定直线数,列出这些直线各种相交的情况下对应的交点数。
首先n条直线,排列组合下,最多能有C(n,2)=n(n-1)/2个交点。
子问题划分:
m条直线,划分为两部分,一部分为A,A中所有线相互平行,剩下的部分为B,假设B里面有r条直线,相交情况未知,可能部分相交,部分平行,但是B中绝没有和A平行的线了。
这样m条直线的交点数 = (m-r)条平行线与r条直线交叉的交点数 + r条直线的交点数
(1)注意到(m-r)条平行线和r条直线的交点数很好算,因为这里不考虑r条直线内部的交点了,所以所有的交点必然落在m-r条平行线上,又因为r条直线没有一条和m-r条直线平行了,所以对于m-r条直线中的每一条,它都和r条直线相交,故而共有(m-r)*r个交点。在m和r确定的情况下,此值确定。
(2)r条直线的交点数,r条直线的交点数有不止一种方案,但是注意到这些交点数的计算方式和求m条直线交点数一样,于是就是一个子问题,递归就可以写出了。
递归函数定义:
定义int func(int lines, int nodes)
lines表示此时的直线数,nodes表示交点数,func的返回值为1时,表示这些直线存在交点数为nodes的情况,反之为0。
由于递归重复较多,用rec[m][n]记忆func(m,n)的值避免重复运算。
如何计算func的值?如果m条直线存在n个交点,由于 n = (m-r)*r + r条直线的交点数
那么 r条直线的交点数 = n -(m-r)*r,也就是说 r 条直线也存在交点数为 n -(m-r)*r 的情况,也就是说func(r,n-(m-r)*r)==1
这样func(lines, nodes)的pseudo code表示如下:
{
if rec[lines][nodes]已经赋值了
return rec[lines][nodes]
for(r = lines; r > 0;r--){
func(i,nodes - (lines-r)*r);
if 这些func返回值有一个>0
return 1;
rec[lines][nodes] = 1;
else
return 0;
}
}
代码如下(C++实现):
2 #include <iostream>
3 using namespace std;
4
5 int rec[21][191];
6
7 int func(int lines, int nodes){
8 if (rec[lines][nodes] > -1)
9 return rec[lines][nodes];
10 int r = lines-1, sum = 0;
11 for(;r > 0;r--){
12 int n = nodes - (lines-r)*r;
13 if (n >= 0) sum += func(r,n);
14 }
15 rec[lines][nodes] = (sum > 0 ? 1 : 0);
16 return rec[lines][nodes];
17 }
18
19 int main(){
20 memset(rec, -1, sizeof(rec));
21 int i = 1, j = 0;
22 for(i = 0;i < 21;i++)
23 rec[i][0] = 1;
24 for (i = 1;i < 191;i++)
25 rec[1][i] = 0;
26 int numLines = 0;
27 while (cin >> numLines){
28 cout << "0";
29 for(j = 1;j <= numLines*(numLines-1)/2;j++)
30 if(func(numLines,j) > 0)
31 cout << " " << j;
32 cout << endl;
33 }
34 return 0;
35 }