递归、加法原理,如何分解问题(独立且完备的划分)

来源:互联网 发布:gis软件使用 编辑:程序博客网 时间:2024/05/29 09:23

加法原理适用于做一件事有n种独立不相交且完备的方向,每个方向上有ai种方案,则总的方案数就是 a1 + a2 +... + an

例题:把n个数分为k个非空子集,有多少种分法?

分解问题:第一个集合里放多少个数把原问题的解分成了独立且完备的若干方向,分别解每个方向上的方案数,然后相加

memo = [[-1] * (1000) for i in xrange(1001)]def group(n, k):if k == 0: return 1 if n == 0 else 0if memo[n][k] > 0: return memo[n][k]ans = 0for i in xrange(1, n - (k - 1) + 1):ans += group(n - i, k - 1)  * C(n, i)memo[n][k] = ansreturn ans

还有一种形式的加法原理,适用于最优解问题,还是原问题划分为若干独立且完备的方向,每个方向上的最优解中取最优的。比如背包问题,第一个物品取或不取就是这样一种独立且完备的划分,全局最优解就是这两种情况下的最优解中最优的那个

def backpack(V, W, C, i):if i == len(V) or C == 0: return 0ans = backpack(V, W, C, i + 1)if W[i] <= C: ans = min (ans, backpack(V, W, C - W[i], i + 1));return ans;

分组问题:n个数分成k组,打印所有的方案

如何划分问题:当前数数分到哪个组作为划分,然后递归问题。注意剪枝去重和排除空集。去重:尽量往前堆

def group(A, k):groups = [[] for i in xrange(k)]def dfs(i, allocated):if i == len(A) and allocated == k: print groupselif k - allocated <= len(A) - i:for j in xrange(k):groups[j].append(A[i])dfs(i + 1, allocated + 1 if len(groups[j]) == 1 else allocated)groups[j].pop()if len(groups[j]) == 0: breakdfs(0, 0)

分组问题的应用POJ1011:给定一组小棍子的长度,拼接一组等长的大棍子,求最小的等长长度。

分析:枚举可能的长度,最小为小棍子中最长的那根,最大为所有棍子拼成一根大棍子的长度,这个长度值还必须能被总长度整除(只考虑整数长度)

对于每个可能的长度len,尝试对小棍子进行分组,条件是组长度不能超过len,如果最后能分完,每个组的长度都 <= len, 则其实都是==len,否则如果有小于len的必然就有>大于len的。

def minEqualtick(A):    minLen, total, lower = [sum(A)], sum(A), max(A)    for l in xrange(lower, total):        if total % l != 0: continue        k, groups = total / l, [0] * (total / l)        def group(i):            if i == len(A): minLen[0] = l            else:                for j in xrange(0, k):                    if groups[j] <= l - A[i]:                        groups[j] += A[i]                        group(i + 1)                        groups[j] -= A[i]                    if groups[j] == 0: break        group(0)    return minLen[0], total / minLen[0], total
 

0 0