算法:捞鱼(分鱼)问题

1,252 阅读1分钟

问题描述

将 30 条鱼放入 10 个桶,每个桶可放 0~10 条,有多少种排列

分析

将 n 条鱼放入 k 个桶,每个桶最多 m 条

dp[n,k] = sum(dp[n-1,k-j]), 0<=j<=m

有排列

按照上式求解

func splitFish(bucketNum, fishNum, limit int) int {
	var dp = make([][]int, bucketNum+1)
	for i := range dp {
		dp[i] = make([]int, fishNum+1)
	}
	
	for i := 0; i <= limit; i++ {
		dp[1][i] = 1
	}

	for i := 2; i <= bucketNum; i++ {
		for j := 0; j <= fishNum; j++ {
			for k := 0; k <= limit; k++ {
				if j >= k {
					dp[i][j] += dp[i-1][j-k]
				}
			}
		}
	}
	return dp[bucketNum][fishNum]
}

无排列

无排列的结果集一定是单调递增/减,按照此规则过滤

func splitFish(bucketNum, fishNum, limit int) int {
	var dp = make([][]int, bucketNum+1)
	for i := range dp {
		dp[i] = make([]int, fishNum+1)
	}
    //保存结果集
	var dpRes = make([][][][]int, bucketNum+1)
	for i := range dpRes {
		dpRes[i] = make([][][]int, fishNum+1)
	}
	
	for i := 0; i <= limit; i++ {
		dp[1][i] = 1
		dpRes[1][i] = [][]int{{i}}
	}

	for i := 2; i <= bucketNum; i++ {
		for j := 0; j <= fishNum; j++ {
			for k := 0; k <= limit; k++ {
				if j >= k {
					for l, h := range dpRes[i-1][j-k] {
					    //过滤掉非递增的排列
					    //过滤已经不在维护的排列,若一直递增,则当前长度应 = i-1
						if len(h) == i-1 && k >= h[0] {
							dpRes[i][j] = append(dpRes[i][j], append([]int{k}, dpRes[i-1][j-k][l]...))
							dp[i][j]++
						}
					}
				}
			}
		}
	}
	return dp[bucketNum][fishNum]
}