用动态规划解小朋友分糖问题
为了解决谷歌一道题,想了个非主流的招数。原题是:“10块都相同的糖分给3个小朋友,每个小朋友至少分到一块糖,有几种分配方式?”,细细一想其实就是每个小朋友一块糖的前提下,再分7块糖,就是把7块糖分给3个小朋友的种类数,也就是这个二维数组中DP[2][6]的取值。
这题完全可以用排列组合来做,穷举各种可能性后乘以组合数,答案是36。
用动态规划的思路比较奇特。。总感觉发现了什么奥秘,却又阐释不清楚,大概说一说吧:
拿3块糖分给3个小朋友这个问题作为分析例子:
1、选择一个小朋友不给他糖(怪蜀黍都这么干!),然后把剩下的3块糖分给其他2个小朋友(一共有4种方式)
2、回顾一下,之前算出的值,把2块糖分给3个小朋友有6种方式,在这六种每种的基础上再加一块糖分给三个小朋友,得出这样的分配方式不能够和上边3块糖分给2个小朋友的分配方式重复,最终可以生成6种不重复的分配方式,最后一共有6+4种分配方式。
上个图:
可以看到,左边是3个人分2块糖的分配方式,右边上半部分是2个人分3块糖的分配方式。左边的列通过一次状态转化(再加一块糖给三个小朋友)即可达到右边的列的状态路径已经连上了线。比如{0,0,2}可以通过加一块糖变成{0,0,3}{0,1,2}{1,0,2},但其中第一个小朋友没有分到糖的情况已经在右边上半部分出现过了,于是只能把糖给那个没有糖的小朋友才能产生出新的状态,即{1,0,2},同理{0,2,0}和{0,1,1}都只能对应一种状态。
最后三种状态同理也只能把新加的一块糖给第一个小朋友,于是又对应三种状态(不通的路径用红色标出,绿色标出的是可行状态转化路径)。
#include <stdio.h> static const int S=10; //S块糖 static const int C=3; //C个小朋友 int main() { int DP[C][S]={0}; //初始化 for (int i=0;i<S;i++) { DP[0][i]=1; //分给一个小朋友不管分多少糖都只有一种分法 } for (int i=0;i<C;i++) { DP[i][0]=i+1; //把一块糖分给i个小朋友有i种方法 } for(int i=1;i<C;i++) { for(int j=1;j<S;j++) { DP[i][j]=DP[i-1][j]+DP[i][j-1]; //状态转移公式,比如三块糖分给三个小朋友就等于3块糖分给2个小朋友加上2块糖分给3个小朋友 } } //输出 for(int i=0;i<C;i++) { for(int j=0;j<S;j++) { printf("%d, ",DP[i][j]); } printf("\n"); } system("pause"); }