您的位置:首页 > 其它

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

2016-07-01 17:05 288 查看


一次掷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-1,1)


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

由上式可以推出f(n,p-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
[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);
}

方法二:


[b]以p个面分别出现的次数为参照,[b]n个骰子,每个骰子有p个面
那么我们只要知道这p个面 每个面出现了多少次,依据上述的排序规则 这个序列就确定了
[/b]
然后就转化成了一个方程
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)种情况.[/b]

//计算组合数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);
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息