数学基础——递推关系
问题1:兔子的繁殖
一对兔子在第二个月后才能产下一对新兔子。第n个月后有多少兔子。
分析:第n个月的兔子数f(n),这f(n)个兔子可以分为两个部分,第一部分是上个月留下来的老兔子f(n-1),
和新兔子,新兔子的数目是上个月有能力生育的兔子之和,也就是n-2天的兔子都有生育能力
问题2:多边形的三角剖分。
给定一个凸n边形,把他分成三角形,求有多少种分法。
(和最优三角剖分类似,只不过不再是一个最值了,而是一个方案数)
问题3:火柴
用n个火柴可以拼成多少种不同的数字。火柴可以不必用完。
题目链接:https://uva.onlinejudge.org/external/113/11375.pdf
分析:
1 import java.math.BigInteger; 2 import java.util.Scanner; 3 4 public class Main { 5 6 Main() { 7 int c[] = { 6, 2, 5, 5, 4, 5, 6, 3, 7, 6 }; 8 BigInteger d[] = new BigInteger[2005]; 9 BigInteger sum[] = new BigInteger[2005]; 10 11 for (int i = 0; i <= 2000; i++) { 12 d[i] = new BigInteger("0"); 13 sum[i] = new BigInteger("0"); 14 } 15 16 d[0] = new BigInteger("1"); 17 18 for (int i = 0; i <= 2000; i++) { 19 for (int j = 0; j < 10; j++) { 20 if (!(i == 0 && j == 0) && i + c[j] <= 2000) { 21 d[i + c[j]] = d[i + c[j]].add(d[i]); 22 } 23 24 } 25 } 26 27 for(int i=1;i<=2000;i++) { 28 sum[i] = sum[i].add(sum[i-1]); 29 sum[i] = sum[i].add(d[i]); 30 } 31 32 Scanner in = new Scanner(System.in); 33 while (in.hasNextInt()) { 34 int n = in.nextInt(); 35 if (n == 0) 36 System.out.println("0"); 37 else if (n < 6) 38 System.out.println(sum[n]); 39 else { 40 System.out.println(sum[n].add(new BigInteger("1"))); 41 } 42 } 43 } 44 45 46 public static void main(String[] args) { 47 new Main(); 48 } 49 }
问题4:立方数之和
题目链接:https://uva.onlinejudge.org/external/111/11137.pdf
题意:给定一个数n ,求能够写成的立方形式有多少种。
分析:
就像DP种多阶段决策一样,d(i,j),最大的元素为 i ,和为 j;题目已经给出 i <22;
那么:
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 int main() 6 { 7 int n; 8 unsigned long long d[22][10000+5]; 9 10 11 12 memset(d,0,sizeof(d)); 13 14 d[0][0] = 1; 15 for(int i=1;i<=21;i++) { 16 for(int j=0;j<=10000;j++) { 17 for(int a=0;j+a*i*i*i<=10000;a++) { 18 d[i][j+a*i*i*i] += d[i-1][j]; 19 } 20 } 21 } 22 while(cin>>n) 23 cout<<d[21][n]<<endl; 24 25 return 0; 26 }
问题5:村民排队
题目链接:https://uva.onlinejudge.org/external/111/11174.pdf
题意:
村子里面有n个人来排队,里面有一些父子关系,儿子不能排到父亲的前面,有多少种排法?
分析:
题目肯定是一个森林,设一个超级父亲节点,他下面如上图有三个子节点,
那么排法就有:
一般的:
设节点 i 为根的子树有f(i)种排法:
问题6:带标号的连通图的计数;
数列如上所示:
题目链接:http://poj.org/problem?id=1737
答案是f(n),g(n) 是非联通图个数,那么就有
g(n):先考虑1节点所在的连通分量包含哪些顶点:C(n-1,k-1);
这个连通分量有f(k),其他有h(n-k)种情况。
1 import java.math.BigInteger; 2 import java.util.Scanner; 3 4 public class Main { 5 6 Main() { 7 BigInteger two[] = new BigInteger[5055]; 8 two[0] = BigInteger.ONE; 9 for (int i = 1; i <= 5050; i++) 10 two[i] = two[i - 1].multiply(BigInteger.valueOf(2)); 11 BigInteger h[] = new BigInteger[55]; 12 for (int i = 1; i <= 50; i++) { 13 h[i] = two[i*(i-1)/2]; 14 } 15 16 BigInteger C[][] = new BigInteger[55][55]; 17 C[0][0] = BigInteger.ONE; 18 for (int i = 0; i <= 50; i++) { 19 C[i][0] = C[i][i] = BigInteger.ONE; 20 for (int j = 1; j < i; j++) { 21 C[i][j] = C[i - 1][j].add(C[i - 1][j - 1]); 22 } 23 } 24 BigInteger f[] = new BigInteger[55]; 25 BigInteger g[] = new BigInteger[55]; 26 f[1] = BigInteger.ONE; 27 for (int i = 2; i <= 50; i++) { 28 g[i] = BigInteger.ZERO; 29 for (int j = 1; j < i; j++) { 30 g[i] = g[i].add(C[i - 1][j - 1].multiply(f[j]).multiply(h[i - j])); 31 } 32 f[i] = h[i].subtract(g[i]); 33 } 34 int n; 35 Scanner cin = new Scanner(System.in); 36 while (cin.hasNext()) { 37 n = cin.nextInt(); 38 if (n == 0) 39 break; 40 System.out.println(f[n]); 41 } 42 } 43 44 public static void main(String[] args) { 45 new Main(); 46 } 47 }