uva10253 Series-Parallel Networks

链接

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1194

题解

这题主要难在它的数学建模
可以把最终的 n e t w o r k network network看作一棵树,最后一步如果是并联,那么我就把并联起来的这几部分看作子树,根节点代表并联这种连接方式,根节点代表并联,那么下一层肯定代表串联,再往下一层肯定代表并联
叶子节点就代表一条边
这棵子树需要满足叶子数等于 n n n,对于任何一棵子树,其子树大小都是递增排列的,每个非叶子节点都至少有两个儿子,这棵树就能代表一个 n e t w o r k network network,树的形态个数就是方案数
由于我一开始规定了根节点代表并联,所以答案要乘以 2 2 2才是最终答案( n = 1 n=1 n=1特判)
这个东西怎么统计?
假设 f ( x ) f(x) f(x)表示叶子数为 x x x时树的形态种类

比较暴力的解法

我只需要对 x x x进行整数划分,比如 5 = 1 + 1 + 1 + 2 5=1+1+1+2 5=1+1+1+2,那就是三棵包含 1 1 1个叶子的子树和 1 1 1棵包含 2 2 2个叶子的子树
这种情形对 f ( 5 ) f(5) f(5)的贡献就是 C f ( 1 ) − 3 + 1 3 C f ( 2 ) − 1 + 1 1 C_{f(1)-3+1}^3C_{f(2)-1+1}^1 Cf(1)3+13Cf(2)1+11(可重复选择的组合数)
[ 1 , 30 ] [1,30] [1,30]中的整数分别做整数划分,然后求,这样时间是允许的,因为 30 30 30的划分不是很大

比较快的解法

f ( x ) f(x) f(x)的意义和上面相同
g ( i , j ) g(i,j) g(i,j)表示含有 i i i个叶子的子树,子树的每棵子树所包含的叶子数都不超过 j j j的方案数
边界条件: f ( 1 ) = 1 , g ( 1 , i ) = 1 ( i ≥ 1 ) f(1)=1,g(1,i)=1(i\geq 1) f(1)=1,g(1,i)=1(i1) g ( 0 , i ) = 1 ( i ≥ 0 ) g(0,i)=1(i\geq 0) g(0,i)=1(i0)
转移的时候其实就是考虑当前层叶子数为 j j j的子树的数量, g ( i , j ) = ∑ p = 0 ⌊ i j ⌋ ( g ( i − p j , j − 1 ) C f ( j ) + p − 1 p + g ( i − p j , + ∞ ) C f ( j ) + p − 1 p [ i − p j &gt; 1 , i − p j &lt; j ] ) g(i,j)=\sum_{p=0}^{\lfloor\frac{i}{j}\rfloor}\left(g(i-pj,j-1)C_{f(j)+p-1}^p+g(i-pj,+\infty)C_{f(j)+p-1}^p[i-pj&gt;1,i-pj&lt;j]\right) g(i,j)=p=0ji(g(ipj,j1)Cf(j)+p1p+g(ipj,+)Cf(j)+p1p[ipj>1,ipj<j])
这个 d p dp dp的思想其实是模仿了整数划分的那个 d p dp dp
n n n m m m划分表示为 f ( n , m ) f(n,m) f(n,m),那么考虑 m m m这个数放不放,得到方程 f ( n , m ) = f ( n − m , m ) + f ( n , m − 1 ) f(n,m)=f(n-m,m)+f(n,m-1) f(n,m)=f(nm,m)+f(n,m1)
这题因为大小相同的子树必须一起算,因此多了个枚举 p p p的举动

收获

这题主要难在数学建模
一些看起来无从下手的题,肯定是进行了某种抽象,它最终指向的肯定还是我们学过的某种模型
所以当问题看起来毫无思路时,不妨把它等价成某种我们熟悉的模型

代码

//数学建模、DP 
#include<bits/stdc++.h>
#define maxn 100
#define ll long long
using namespace std;
ll f[maxn], g[maxn][maxn], N;
ll C(ll n, ll m)
{
	ll ans=1, i, j=2;
	for(i=n;i>=n-m+1;i--)
	{
		ans*=i;
		if(ans%j==0 and j<=m)ans/=j++;
	}
	return ans;
}
void dp()
{
	int i, j, k;
	for(i=0;i<maxn;i++)g[0][i]=1;	//全放j的情况 
	for(i=1;i<maxn;i++)g[1][i]=1;
	f[1]=1;
	for(i=2;i<maxn;i++)
	{
		for(j=1;j<i;j++)
		{
			g[i][j]=g[i][j-1];
			for(k=1;k*j<=i;k++)
			{
				g[i][j]+=g[i-k*j][j-1]*C(f[j]+k-1,k);
				if(i-k*j<j and i-k*j>1)g[i][j]+=g[i-k*j][maxn-1]*C(f[j]+k-1,k);
			}
		}
		for(j=i;j<maxn;j++)g[i][j]=g[i][j-1];
		f[i]=g[i][i-1];
	}
}
int main()
{
	ll N;
	dp();
	while(scanf("%lld",&N),N)
	{
		if(N!=1)printf("%lld\n",f[N]<<1);
		else printf("1\n");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值