石子游戏有两种:一种是博弈方面;另一种是合并规划方面的。前者需要太厉害的判断,而且要用到很多数学方面的结论,所以俺不会。这里主要探讨一下石子归并方面的问题与算法。
一. 简单的石子合并
问题类似于背包问题:给出n堆石子的数量,请你求出:由这n堆石子可以合并得到的所有重量;或求出合并成两堆之后的最小质量差是多少。
其实上一篇谈到的“数字N能否表示成若干个不相同的阶乘的和”就是这个问题的前半部分,前面是采用搜索得到所有可以组成的数量,本文下面采用动态规划来求得所有可以组成的数量。然后再解决该问题的后半部分。
(1)问题前半部分
动态规划:首先是定义好状态,这个问题是求所有可以合并后的和,那么我们就可以利用标号的方法来标记所有可以合并得到的和。数组num[n]表示读入的n堆石子数量。利用bool数组f[N],(N为这n堆石子的重量之和),状态f[i] = true 表示可以得到和为i,否则=false,表示不可以得到和为i,有:
边界条件f[0] = true ---- 尽管没实际意义,但对于DP来说却是必需的
f[i+num[j]] = f[i+num[j]] | f[i]; ---- i满足的条件是:0 <= i <= total – num[j]。方程的意思就是:总数为i的状态可以加上j组成总合为 i+j 的状态。
注意点:由于动态规划需要满足无后效性,那么如果顺推,i+num[j]的顺推必然对后面的造成影响,因此递推的方向需要改成反向,这样就不会影响了:
算法:石子合并的动态规划
过程:调用DP_Stone(n)得到所有标记好的可以组成的和
DP_Stone1 (int n) {
int i, j, total;
f[0] = true; total= 420000; // 先置可以组成和为 0,total为总数量
for (j = 0; j < n; j++) {
for (i = total-num[j]; i >= 0; i--)