地址:http://acm.bit.edu.cn/mod/programming/view.php?a=494
DP好题。将n个苹果分入k的盘子,盘子可以空着,问不同的方法总数(1,2,2和2,1,2算一种)
建立dp[a][b],表示将a个苹果分入b个盘子的方法总数。初始条件为dp[a][0](0<a<101)=0,dp[0][a]](0<a<101)=1(很科学,将a个苹果分入0个盘子——不行,0种,将0个苹果分入a个盘子——全不放,1种)。那么状态转移方程是?
我们将dp[a][b]的这些方法分成两种。一种是含0的,即有盘子没放苹果的情况。这一部分情况与dp[a][b-1]的所有情况一一对应。另一种是所有盘子都至少装一个的情况,这一部分情况与dp[a-b][b]的所有情况一一对应,对应方法是每个盘子都差一个。故dp[a][b] = dp[a][b-1] + dp[a-b][b] (a>=b)及dp[a][b] = dp[a][b-1] (a<b)。
这样说可能还不好理解,我们拿实例来看:如样例 dp[7][3]=8 ,这八种方法分别是(1,1,5)(1,2,4)(1,3,3)(2,2,3)(0,1,6)(0,2,5)(0,3,4)(0,0,7)其中(1,1,5)(1,2,4)(1,3,3)(2,2,3)这四种属于第二类情况,与dp[7-3][4]的四种情况一一对应,即(0,0,4)(0,1,3)(0,2,2),(1,1,2),可以看到,对应关系为-1,-1,-1每个篮子都少一个;(0,1,6),(0,2,5),(0,3,4)(0,0,7)这四种都属于第一类情况,与dp[7][3-1]的四种情况一一对应即(1,6),(2,5),(3,4),(0,7),可以看到,对应关系为少了一个空盘子。因此dp[7][3]=dp[4][3]+dp[7][2]=8.当然,dp[4][3]和dp[7][2]也可以继续状态转移,故可以DP求解。
注意到状态转移方程要求的是上一行和这一行的前面的状态,故二重循环行嵌套列可以将dp数组求出。
(神方法啊,谁想出来的。。)
#include<iostream>
using namespace std;
int dp[101][101];
int main()
{
int i,j,n,k;
for(i=0;i<101;i++) //初始条件
{
dp[i][0]=0,dp[0][i]=1;
}
for(i=1;i<101;i++)
{
for(j=1;j<101;j++)
{
if(i>=j) dp[i][j]=dp[i-j][j]+dp[i][j-1]; //状态转移方程
else dp[i][j]=dp[i][j-1];
}
}
while(scanf("%d%d",&n,&k)!=EOF)
{
printf("%d\n",dp[n][k]);
}
return 0;
}