UVa 10253 (组合数 递推) Series-Parallel Networks

《训练之南》上的例题难度真心不小,勉强能看懂解析,其思路实在是意想不到。

题目虽然说得千奇百怪,但最终还是要转化成我们熟悉的东西。

经过书上的神分析,最终将所求变为:

共n个叶子,每个非叶节点至少有两个子节点的 树的个数f(n)。最终输出2 × f(n)

首先可以枚举一下根节点的子树的叶子个数,对于有i个叶子的子树,共有f(i)种,

设d(i, j)表示每棵子树最多有i个叶节点,一共有j个叶节点的方案数。

所求答案为d(n-1, n)

假设恰好有i个叶子的子树有p棵,因为每个子树互相独立,所以对于p个有i个叶子的子树,共有C(f(i)+p-1, p)种情况,重复元素的全排列。

d(i, j) = sum{C(f(i)+p-1, p) × d(i-1, j-p×i) | p >= 0 且 p×i <= j }

边界:

d(i, 0) = d(i, 1) = 1 (i >= 1), d(0, 0) = 1

 1 #include <cstdio>
 2
 3 const int maxn = 30;
 4 long long d[31][31], f[31];
 5
 6 long long C(long long n, long long m)
 7 {
 8     long long ans = 1;
 9     if(m > n - m) m = n - m;
10     for(int i = 0; i < m; i++)
11     {
12         ans *= n - i;
13         ans /= i+1;
14     }
15     return ans;
16 }
17
18 int main()
19 {
20     f[1] = 1;
21     int n = maxn;
22     d[0][0] = 1;
23     for(int i = 1; i <= n; i++) d[i][0] = d[i][1] = 1;
24     for(int i = 1; i <= n; i++)
25     {
26         for(int j = 2; j <= n; j++)
27         {
28             for(int p = 0; p * i <= j; p++)
29                 d[i][j] += C(f[i]+p-1, p) * d[i-1][j-p*i];
30         }
31         f[i+1] = d[i][i+1];
32     }
33
34     while(scanf("%d", &n) == 1 && n)
35         printf("%lld\n", n == 1 ? 1 : f[n] * 2);
36
37     return 0;
38 }

代码君

时间: 2024-08-05 23:39:12

UVa 10253 (组合数 递推) Series-Parallel Networks的相关文章

UVA 10237 - Bishops(递推)

UVA 10237 - Bishops 题目链接 题意:问一个n * n棋盘能放k个主教(攻击斜线)的方案数. 思路:递推,首先考虑一个问题,在一个n?n棋盘上,放k个车的方案数. 那么设dp[i][j]为i行用了j个车的方案数,由于每行只能放一个车,那么考虑i行放不放车,如果放车,那么能放的位置有n?(j?1)个位置,为dp[i?1][j?1]?(n?(j?1)). 如果不放那么情况为dp[i?1][j]. 所以递推式为dp[i][j]=dp[i][j?1]+dp[i?1][j?1]?(n?(

UVA 1425 - Metal(递推)

UVA 1425 - Metal 题目链接 题意:给定一个金属板,上面有一些点,现在有一台切割机,要切割出单调四边形,由所有点组成,问有多少种情况. 思路:递推,设dp[i][j],i为上面点,j为下面点,现在多添加一个点k进来,那么原来的dp[i][j]必然要有一维为k - 1,枚举另外一维就是所有情况.然后再添加点进来的过程中还要考虑能不能加进来,写一个判断函数,把连接线之间所有点枚举一边利用向量叉积去判断即可,如果是上面的线,就不能有点在上面,如果是下面的线,就不能有点再下面. 代码: #

组合数递推算法

主要式子:C(n,k)=C(n-1,k-1)+C(n-1,k),C(n,k)表示从n个物品中挑选k个物品的所有组合数. 1 #include<stdio.h> 2 #include<string.h> 3 #define N 10 4 int c[N][N]; 5 void init() 6 { 7 memset(c,0,sizeof(c)); 8 for(int i=0;i<=N;i++) 9 { 10 c[i][0]=1; 11 c[i][i]=1; 12 for(int

uva 11375 - Matches(递推)

题目链接:11375 - Matches 题目大意:给出n根火柴,问说能组成多少种数字,要求说0不能打头. 解题思路:d[i]表示i根火柴能够组成的数量,d[i+c[j]] = d[i+c[j]] + d[i]; 最后dp[i]表示小于等于i根火柴能组成的数量,dp[i]=∑jidp[j]. 高精度. #include <cstdio> #include <cstring> #include <iostream> using namespace std; const i

uva 279 - Spin(递推)

题目链接:uva 279 - Spin 题目大意:进行一个游戏,给出初始状态,要求问说最少多少步可以让所有的环移动出来.移动规则如图所示. 解题思路:一开始以为是隐式图搜索,写完TLE了.后来发现这道题和汉诺塔是一个思路,都是采取最优策略,并且说左边环的状态不会影响右边环.所以dp[i]表示从右边数,第i个为v,其他均为h的步数(由全h变换至). 模拟最优过程有dp[i]=dp[i?1]?2+i?2?1 对已给定状态,可看做由全h变换到该状态的步数.根据容斥原理,第奇数个v为加,偶数个v为减.最

UVA - 624CD(递推+ 路径打印)

题目: UVA - 624CD(递推+ 路径打印) 题目大意:给出一组数据,给定一个N,问这些数据能否拼凑出不大于N的最接近N的数据,可以的话输出最接近N的数据,并且打印出最长路径(要求要找输入的顺序). 解题思路:dp[j]:代表凑出J这个数值最多需要几个数.d[j] = Max (d[j - v[i]] + 1. 打印路径,如果取得是最小值,那么顺着dp标记的值的减小就可以找到路径,但是取的是最大值,这样它的下一个并不能直接靠dp数组的值来判断,而是要判断到最后是否最终的值等于0.用回溯.

UVA 12034 - Race(递推)

UVA 12034 - Race 题目链接 题意:给定n匹马,要求出可能的排名情况(可能并列) 思路:递推,dp[i][j]表示i匹马的时候有j种不同名次,那么dp[i][j]可以由dp[i - 1][j - 1]插入j个不同位置得来,或者由dp[i - 1][j]放入已有j的名次得来,得到递推式dp[i][j] = j * (dp[i - 1][j - 1] + dp[i - 1][j]); 然后对于n的答案为sum{dp[n][j]} (1 <= j <= n) 代码: #include

UVa 1645 Count (递推,数论)

题意:给定一棵 n 个结点的有根树,使得每个深度中所有结点的子结点数相同.求多棵这样的树. 析:首先这棵树是有根的,那么肯定有一个根结点,然后剩下的再看能不能再分成深度相同的子树,也就是说是不是它的约数.那么答案就有了, 我们只要去计算n-1的约数有多少棵不同的树,然后就有递推式了. 代码如下: #pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include <str

UVa 557 (概率 递推) Burger

题意: 有两种汉堡给2n个孩子吃,每个孩子在吃之前要抛硬币决定吃哪一种汉堡.如果只剩一种汉堡,就不用抛硬币了. 求最后两个孩子吃到同一种汉堡的概率. 分析: 可以从反面思考,求最后两个孩子吃到不同汉堡的概率. 因为最后两个汉堡是不同的,所以前面的2n-2个孩子吃汉堡之前一定都是要抛硬币的. 所以,吃两种汉堡的孩子人数相等,都是n-1个. 令,对于2n个孩子吃汉堡,所求概率为1 - f(n-1) 我们还可以递推f, 1 #include <iostream> 2 #include <cst