排列组合
Problem Description
有n种物品,并且知道每种物品的数量。要求从中选出m件物品的排列数。例如有两种物品A,B,并且数量都是1,从中选2件物品,则排列有"AB","BA"两种。
Input
每组输入数据有两行,第一行是二个数n,m(1<=m,n<=10),表示物品数,第二行有n个数,分别表示这n件物品的数量。
Output
对应每组数据输出排列数。(任何运算不会超出2^31的范围)
Sample Input
2 2 1 1
Sample Output
2代码:#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;int d[14][14];void fun(int m)
{
int i;
d[1][0]=1;
d[1][1]=1;
for(i=2;i<=m;i++)
{
d[i][0]=1;
for(int j=1;j<i;j++)
d[i][j]=d[i-1][j]+d[i-1][j-1];//该数组的d[i][j]代表已经取得i个数中的j个数,它等于取得第i-1个数中的j个与取得第i-1个数中的j-1个数的加和
d[i][i]=1;
}
}int main()
{
int n,m,i,j,k,sum;
int a[15],c[12];
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(a,0,sizeof(a));
memset(c,0,sizeof(c));
memset(d,0,sizeof(d));
fun(m);
for(i=0;i<n;i++)
scanf("%d",&a[i]);
for(i=0;i<=a[0]&&i<=m;i++)//先将列出的母函数中的第一项初始化
c[i]=d[m][i];
for(i=1;i<n;i++)//是后面的n-1项
{
if(a[i])
for(j=m;j>=0;j--)
if(c[j])
for(k=1;k+j<=m&&k<=a[i];k++)
c[k+j]+=d[m-j][k]*c[j];
}
printf("%d/n",c[m]);
}
return 0;
}关于母函数,我是看到这个人的解释才看懂了的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38#include <iostream> using namespace std; // Author: Tanky Woo // www.wutianqi.com const int _max = 10001; // c1是保存各项质量砝码可以组合的数目 // c2是中间量,保存没一次的情况 int c1[_max], c2[_max]; int main() { //int n,i,j,k; int nNum; // int i, j, k; while(cin >> nNum) { for(i=0; i<=nNum; ++i) // ---- ① { c1[i] = 1; c2[i] = 0; } for(i=2; i<=nNum; ++i) // ----- ② { for(j=0; j<=nNum; ++j) // ----- ③ for(k=0; k+j<=nNum; k+=i) // ---- ④ { c2[j+k] += c1[j]; } for(j=0; j<=nNum; ++j) // ---- ⑤ { c1[j] = c2[j]; c2[j] = 0; } } cout << c1[n] << endl; } return 0; }
我们来解释下上面标志的各个地方:
① 、首先对c1初始化,由第一个表达式(1+x+x2+..xn)初始化,把质量从0到n的所有砝码都初始化为1.
② 、 i从2到n遍历,这里i就是指第i个表达式,上面给出的第二种母函数关系式里,每一个括号括起来的就是一个表达式。
③、j 从0到n遍历,这里j就是只一个表达式里第j个变量,比如在第二个表达式里:(1+x2+x4….)里,第j个就是x2*j.
③ k表示的是第j个指数,所以k每次增i(因为第i个表达式的增量是i)。
④ 、把c2的值赋给c1,而把c2初始化为0,因为c2每次是从一个表达式中开始的