有一个n位数递增的数列,每位上的数最小可以为1,最大为m,问总共有多少种情况,写出一个通用的公式。
比如n=2,m=3时有6种情况
[1 1]
[1 2]
[1 3]
[2 2]
[2 3]
[3 3]
n=3,m=4时有20种情况
[1 1 1]
[1 1 2]
[1 1 3]
[1 1 4]
[1 2 2]
[1 2 3]
[1 2 4]
[1 3 3]
[1 3 4]
[1 4 4]
[2 2 2]
[2 2 3]
[2 2 4]
[2 3 3]
[2 3 4]
[2 4 4]
[3 3 3]
[3 3 4]
[3 4 4]
[4 4 4]
C(n+m-1,n)
设a[i]表示数字i出现的次数,那么a[1]+a[2]+..+a[m]=n,其中所有a[i]都是自然数
可以发现,一组这个方程的解恰好对应一个题目所求情况,因此题目所求方案数等于这个方程的解数
这个方程的解数量,直接用隔板法就可以求
C(n+m-1,n) 是正解。
但方程的解数是如何对应题目所求情况呢?
@Xiangism:
a[i]表示序列中数字i出现的次数,a[1]+a[2]+..+a[m]=n,
那么m表示可以出现的数字有1,2,..,m,n就表示各个数字出现的总次数(也就是序列长度)
比如n=9,m=3,a[1]=2,a[2]=3,a[3]=4
对应的序列就是[1,1,2,2,2,3,3,3,3]
其实吧,这个“...恰好对应...”只是为了表述方便,只是想说明这个方程的一个解是一个合法序列的另一种表示方法,要更严格我就说不清楚了。。。
实际上用程序来实现也很简单
#include <stdio.h>
int a[10005];
int n,m;
void fun(int x,int value)
{
if(x==n)
{
for(int i=0;i<n;i++)
{
printf("%d ",a[i]);
}
printf("\n");
return;
}
for(int i=value==0?1:value;i<=m;i++)
{
a[x] = i;
fun(x+1,i);
}
}
int main()
{
scanf("%d%d",&n,&m);
fun(0,0);
}
不错。我是用非递归的方式实现的
type ListFactory struct {
// 总共有的元素个数
Count int
// 每个元素最大可取的数
Max int
now []int
}
func NewList(count, max int) *ListFactory {
r := ListFactory{
Count:count,
Max:max, }
r.now = make([]int, count)
r.Init(1)
return &r
}
func (n *ListFactory) Init(c int ) {
for i := 0; i < n.Count-1; i += 1 {
n.now[i] = c
}
n.now[n.Count-1] = c-1
}
func (n *ListFactory) incr() bool {
if n.now[n.Count-1] < n.Max {
n.now[n.Count-1] += 1
return true
}
i := n.Count-1
for ; i >= 0; i -= 1 {
if n.now[i] != n.Max {
break
}
}
if i < 0 {
return false
}
if i == 0 && n.now[0] == n.Max{
return false
}
n.now[i] += 1
v := n.now[i]
for j := i+1; j < n.Count; j += 1 {
n.now[j] = v
}
return true
}
func (n *ListFactory) Create() (bool, []int) {
if n.incr() {
return true, n.now
} else {
return false, nil
}
}
@Xiangism: 这是什么语言?
@Shendu.CC: go语言