题目:http://acm.jlu.edu.cn/joj/showproblem.php?pid=2039
算法:动态规划
分析如下:
设函数f(n,k)表示大小为n的地图中放k个blockhouse的方法数。那么,放置blockhouse的方法无外乎以下两种:
1. 一个blockhouse放在了第一行第一个位置。
2. 没有任何blockhouse放在第一行第一个位置。
题目中所描述的地图的形状与线性代数中的一种特殊矩阵形式非常像,为了方便分析动态规划的递推式,把一个大小为N的地图写成矩阵的形式,可以放置blockhouse的位置用1表示,其余位置用0表示:
以上是一个大小为5的地图,以此为例。
假如有一个blockhouse放在第一行第一个位置,则剩下的blockhouse只能放在余下的大小 为n-1的地图上,此时方法数为f(n-1,k-1)。
假如没有任何blockhouse放在第一行第一个位置,则拿掉该位置后地图变成如下形状:
假设在这种形状的地图上放置k个blockhouse的方法数为g(n,k),那么由此可以得到f(n,k)的递推式:
类似于f(n,k)的分析,g(n,k)又能分为两种情况:第一行的那个位置放了一个blockhouse;第一行不放blockhouse。如此,又可以得到g(n,k)的递推式:
对于边界情况,有:
上述f和g相互调用,可以笔算一下f(3,2)来验证不会死循环。其实,从上述边界情况的式子也可以看出来,调用f(n,k)只有当n>k时才会递归调用,而递归调用时f(n,k)分为三部分:f(n-1,k-1),f(n-1,k),g(n-1,k-1),第二部分f(n-1,k)会一直减小到0为止。另外两部分数据规模在一直减小。不会出现死循环。
下面是AC过的代码:
- #include <cstdio>
- double f[32][32], g[32][32];
- double ff(int n, int k);
- double gg(int n, int k);
- double ff(int n, int k) {
- if(n < k) return 0;
- if(f[n][k]) return f[n][k];
- return f[n][k]=ff(n-1,k-1)+gg(n,k);
- }
- double gg(int n, int k) {
- if(n<=k) return 0;
- if(g[n][k]) return g[n][k];
- return g[n][k]=ff(n-1,k)+gg(n-1,k-1);
- }
- int main() {
- int T,C;
- freopen("in.txt", "r", stdin);
- for(int i = 0; i < 32; i++) {
- f[i][1]=2*(i-1)+1;
- g[i][1]=2*(i-1);
- f[i][i]=1;
- }
- while(scanf("%d%d", &T, &C)!=EOF) {
- printf("%.0lf/n", ff(T, C));
- }
- return 0;
- }