DP算法解释

稍微解释下:
BitSet isSolved,isSuit
中的isSolved.get(n,k)表示(n,k)是否已经求解过
isSuit.get(n,k)为true表示n个人可能为k场
简记isSuit.get(n,k)为s[n,k].

n个人能够比k场实际上是问: n能否存在一个分解 xi(xi>0)使得
Sum{xi} =n, Sum{xi*xj: i!=j} =k;
注意到 Sum{xi}*Sum{xi} = Sum{xi*xj}

= Sum{xi*xi} +2 *Sum{xi*xj: i!=j}
又易知 n <=Sum{xi*xi} <= n*n
故可得k的取值范围为: 0<=k <= n*(n-1)/2
因此我们只需判断 k在 [0,n*(n-1)/2]中的取值.

下面我们来推导DP状态转移方程:

假设n存在分解 n = Sum{xi: 1<=i<=L}
使得 k = Sum{xi*xj: 1<=i,j<=L,i!=j}
有: k = Sum{x1*xi: i!=1} +Sum{xi*xj: 2<=i,j<=L,i!=j}
= x1*(n-x1) +Sum{xi*xj:2<=i,j<=L,i!=j}
可以看出: {xi: 2<=i<=L}是 k- x1*(n-x1)的一个满足条件的分解
从而我们有:
s[n,k] = OR{s[n-m,k-m*(n-m)] : 1<=m其中OR表示逻辑或运算.

6.11添加:对offset()方法的说明
对于每一个n,我只需计算s[n,0],s[n,1],....,s[n,n*(n-1)/2]
很容易可以想到可以用一个二维boolean数组来保存s的值:
boolean[][] s =new boolean[MAX+1][]; //用MAX+1是因为把n=0的情形也包含进出了
然后:
s[n] =new boolean[n*(n-1)/2+1]
不过,这里我没有采用boolean数组,而是采用一个BitSet(后面的C代码用的是一个char数组,然后用其每一个bit保存一个值).
BitSet可以看成是一个一维bit数组,但这里我们就要考虑如何将原来的二维boolean数组"拉直"成一维数组了:
显然,我们只要知道s[n]对应的BitSet位置就可以了,这就是offset方法要干的事.
由于s[0]要占1个位,s[1]要占一个位,....,更一般的:
s[m]的取值是0到m*(m-1)/2
因此,s[m]要占 1+m*(m-1)/2 个位 (呵呵,不要把这个1漏了)
这样,我们可以计算s[n]的在BitSet的偏移位置应该是前s[0],...s[n-1]所占位置的总和,也就是:
Sum{1+m*(m-1)/2 : 0<=m<=n-1}
唔,这个求和公式比1加到100要难一些(当然,如果你和我一样是学数学的话就不会觉得难了)
总而言之,求和的结果就是:

引用

offset(n) =Sum{1+m*(m-1)/2 : 0<=m<=n-1}
=n*(n-1)*(n-2)/6 +n

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/12467/viewspace-148155/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/12467/viewspace-148155/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值