最近在看一些动规的题目,看到这个题首先想到的是将n条直线分开,分成两部分。第一部分有(n-i)条,两两平行,第二部分有i条,与第一部分不平行。于是,总的交点数 = 第一部分的交点数 + 第二部分的交点数 + 两部分之间的交点数。第一部分互相平行,交点数为0;第二部分为原问题的一个子问题;两部分之间的交点数等于两部分的直线条数的乘积。用m[k]来表示k条直线的交点数,于是 m[n] = m[i]+i*(n-i) (0<=i<=n-1,假设 i 已知)。于是问题规模变小了。
考虑到n条直线的交点数最多可以取 n(n-1)/2,题目中n最大取20,即交点数最多为190,定义二维数组m[21][1000],m[i][j] 表示 i 条直线可以产生的第 j 种不同的交点数。一维数组n[21],n[k]表示k条直线产生的最多交点数。
先计算各个子问题,即从m[0]开始,逐次计算m[i],直到m[n],为第一层循环。
对于每个k,m[k] = m[i]+i*(k-i),0<=i<=k-1,依次考虑 i 的不同取值,计算出的不同m[k]利用插入排序方法存入 m[k][ ]中,为第二层循环。
对于每个m[i],有n[i]种不同取值,为第三层循环。
算法实现如下:
#include <iostream>
#include <cstring>
using namespace std;
int m[21][1000];
int n[21];
void insert_sort(int a[],int &n,int x)
{
int i,j;
if(x>a[n-1])
{
a[n]=x;
n++;
}
else if(x==a[n-1])
return;
else
{
for(i=0;i<n-1;i++)
{
if(x==a[i]) return;
if(x>a[i] && x<a[i+1])
{
for(j=n-1;j>=i+1;j--)
a[j+1]=a[j];
a[i+1]=x;
n++;
}
}
}
}
int main()
{
int i,j,k,t,q;
memset(m,0,sizeof(m));
memset(n,0,sizeof(n));
for(i=0;i<=20;i++)
{
m[i][0]=0;
n[i]=1;
}
for(i=1;i<=20;i++)
{
for(j=0;j<=i-1;j++)
{
q=(i-j)*j;
for(k=0;k<n[j];k++)
{
insert_sort(m[i],n[i],m[j][k]+q);
}
}
}
while(cin>>t)
{
cout <<m[t][0];
for(i=1;i<n[t];i++)
cout <<" " <<m[t][i];
cout <<endl;
}
return 0;
}