DP--划分问题(掷骰子)

来源:互联网 发布:飞凡软件 编辑:程序博客网 时间:2024/06/06 08:48

一次掷n个骰子,每个骰子有p个面,问有多少种情况?

要考虑重复情况,例:2个骰子,3个面,1,1,2和2,1,1是一种情况

对于这种情形 我们一般进行排序处理 把所有情况所对应的数从小到大排序
a1<=a2<=a3<=...<=an。
比如1,1,2和2,1,1就相当于1,1,2了
再比如 3,1,2 2,3,1 都相当于1,2,3
这样每种情况都与一个序列一一对应不是吗?

方法一:

和划分问题一类似,考虑动态规划方程

第一个骰子为1时,剩下的骰子有p   个面选择,f(n-1,p)

第一个骰子为2时,剩下的骰子有p-2个面选择,f(n-1,p-1

……

第一个骰子为p时,剩下的骰子有1  个面选择,f(n-11

所以得到f(n,p)=f(n-1,p)+f(n-1,p-2)+……+f(n-1,1

由上式可以推出f(np-1)=f(n-1,p-2)+f(n-1,p-3)+……+f(n-1,1

两式联立得递推公式 f(n,p)=f(n-1,p)+f(n,p-1)


当n==1时,f(n,p)=p;当p==1时,f(n,p)==1

//DP数组记忆化int DP(int n,int p){    if(n==1)return p;    else if(p==1) return 1;    int dp[n+1][p+1]={1},i,j;    for(i=1;i<=n;i++)        for(j=1;j<=p;j++)    {        if(i==1) dp[i][j]=j;        else if(j==1) dp[i][j]=1;        else dp[i][j]=dp[i-1][j]+dp[i][j-1];    }    return dp[n][p];}//递归int f(int n,int p){    if(n==1) return p;    else if(p==1) return 1;    else return f(n,p-1)+f(n-1,p);}


方法

以p个面分别出现的次数为参照n个骰子,每个骰子有p个面 那么我们只要知道这p个面 每个面出现了多少次,依据上述的排序规则 这个序列就确定了
然后就转化成了一个方程
x1+x2+x3+...xp=n 有多少组非负整数解
(xi代表第i个面出现了多少次)

对于这个方程 我们令ai=xi+1
则x1+x2+x3+...xp=n的非负数解与方程 a1+a2+a3+...ap=n+p的正数解一一对应

a1+a2+a3+...ap=n+p 这个方程的正数有多少组解呢? 答案是C(n+p-1,p-1)种

因为可以看成是有n+p个1 然后中间插入p-1个板子 隔板法求得方程的解数
所以一次掷n个骰子,每个骰子有p个面 有C(n+p-1,p-1)种情况.

//计算组合数C(n,m)int C(int n,int m){    if(m>n/2) m=n-m;    double result=1;    int i=n,j=m,t;    while(i>=n-m+1||j>=1)    {        if(i>=n-m+1)        {            result*=(i*1.0);            i--;        }        if(j>=1)        {            result/=(j*1.0);            j--;        }    }    return (int)result;}int Formula(int n,int p) // C(n+p-1,p-1){    return C(n+p-1,p-1);}




0 0