问题描述:用关系 " < < < "和 " = = = "将3个数 A 、 B 、 C A、B、C A、B、C依序排列时,有13种不同的序关系: A = B = C , A = B < C , A < B = C , A < B < C , A < C < B , A = C < B , B < A = C , B < A < C , B < C < A , B = C < A , C < A = B , C < A < B , C < B < A A=B=C,A=B<C,A<B=C,A<B<C,A<C<B,A=C<B,B<A=C,B<A<C,B<C<A,B=C<A,C<A=B,C<A<B,C<B<A A=B=C,A=B<C,A<B=C,A<B<C,A<C<B,A=C<B,B<A=C,B<A<C,B<C<A,B=C<A,C<A=B,C<A<B,C<B<A。若要将 n n n 个数依序进行排列,试设计一个算法,计算出有多少种不同的序关系(是计算数目,不是给出各种排列)。要求算法的空间复杂性为 O ( n ) O(n) O(n),时间复杂性为 O ( n 2 ) O(n^2) O(n2)。
若用 A [ i , j ] A[i,j] A[i,j] 表示用 j j j 个" < < < "号连接 i i i 个数时产生的不同序关系数,并约定当 j > i − 1 j>i-1 j>i−1 时, A [ i , j ] = 0 A[i,j]=0 A[i,j]=0 ,另外显然 A [ i , 0 ] = 1 , i ∈ [ 1 , n ] A[i,0]=1, i\in[1,n] A[i,0]=1,i∈[1,n] 。
A [ i , j ] = ( j + 1 ) ( A [ i − 1 , j − 1 ] + A [ i − 1 , j ] ) A[i,j]=(j+1)(A[i-1,j-1]+A[i-1,j]) A[i,j]=(j+1)(A[i−1,j−1]+A[i−1,j])
考虑 i i i 个数是由 i − 1 i-1 i−1 个数增加一个 x x x 得到的,则分为两种情况: ①增加 x x x 之前有 j − 1 j-1 j−1 个 " < < < " ; ②增加 x x x 之前有 j j j 个 " < < < "。另外, x x x 有 j + 1 j+1 j+1 种可能性,这是因为,若把相等数视为同一个数,则 j j j 个 “ < < <” 的连接 i i i 个数的情况为: S 1 < S 2 < ⋯ < S j + 1 S_1 < S_2 < \cdots < S_{j+1} S1<S2<⋯<Sj+1,其中 S k S_k Sk 是所有数值等于 x k x_k xk 的数的集合。
//动态规划法
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int[][] dp=new int[n+1][n+1];
for(int i=1;i<=n;i++){
for(int j=0;j<=i-1;j++){
if(j==0){
dp[i][0]=1;
}else{
dp[i][j]=(j+1)*(dp[i-1][j-1]+dp[i-1][j]);
}
}
}
int sum=0;
for(int i=0;i<=n;i++){
sum+=dp[n][i];
}
System.out.println(sum);
}
}
//递推法
int Orderings( int n)
{
A[0]=1;
for(int j=1;j<=n-1;j++)
A[j]=0;
for(int i=2;i<=n;i++)
for(int j=i-1;j>=1;j--)
A[j]=(j+1)*(A[j-1]+A[j]);
int total=0;
for(int j=0;j<=n-1;j++)
total+=A[j];
return total;
}
递推法,复杂度不满足题目要求:
简单的说就是:存在K,使得a1=a2=a3=a4...ak<ak+1....an成立。以<为分界线。后面的那一部分是子问题。
选取K个数的组合数为C(n,k)=c(n-1,k-1)+c(n-1,k);
这是高中的知识,就是说n个人中有一个特例,c(n-1,k-1)表示这个人一定在其中,c(n-1,k)表示这个人不选中。
加上后面的子问题退出递推式为g(n),g(n)表示N个数时的序关系数。g(n)=∑C(n,k)*g(n-k) k from 1 to n。
其实组合问题本来就是一个递归,你要求最后面的值你就得先求前面的值,这也是动态规划的思想之一
递推法代码实现的巧妙之处:代码先计算出 i = 2 i=2 i=2 时对应的 A [ j ] A[j] A[j],当 i = 3 i=3 i=3 时,代码 A [ j ] = ( j + 1 ) ∗ ( A [ j − 1 ] + A [ j ] ) A[j]=(j+1)*(A[j-1]+A[j]) A[j]=(j+1)∗(A[j−1]+A[j])中,右侧 A [ j ] A[j] A[j] 对应的时上一步的结果( i = 2 i=2 i=2 )时,因此得到的当前步( i = 3 i=3 i=3 )的结果,以此类推。