问题 K: 运动会
题目
题目描述
在一次运会上,有一个比赛项目,共有N个人参加比赛,要将这N个人分组,每组人数不少于K个,问有多少种分组方式?
比如有16个运动员,每组人数不少于5个,共有6种分组方式:
(1) 分一组,为16人;
(2) 分二组,分别为11人、5人;
(3) 分二组,分别为10人、6人;
(4) 分二组,分别为9人、7人;
(5) 分二组,分别为8人、8人;
(6) 分三组,分别为6人、5人、5人。
注意:6+5+5,5+6+5,5+5+6为同一种,只算一种分组方式;
比如有16个运动员,每组人数不少于5个,共有6种分组方式:
(1) 分一组,为16人;
(2) 分二组,分别为11人、5人;
(3) 分二组,分别为10人、6人;
(4) 分二组,分别为9人、7人;
(5) 分二组,分别为8人、8人;
(6) 分三组,分别为6人、5人、5人。
注意:6+5+5,5+6+5,5+5+6为同一种,只算一种分组方式;
输入
输入共一行为两个整数N, K。表示有N个运动员分组,每组不少于K个人(1 ≤ K ≤ N ≤ 500)。
输出
输出共一行为一个整数,表示分组数。
16 5
6
解析
这个题就是枚举分组,从一个分组到n/m个分组;然后就是每个分组先分上m个,然后就剩下z=n-i*m个球,分到i组里
然后就转化成一个子问题,就是z个球分到 i 个桶里,球相同桶也相同,这是一个dp的问题。传送门
dp[i][j]:将i个球放进j个箱子中,共有多少种不同的放法
dp初始化:
dp[0][j] = 1; 将0个球放进j个箱子中,不管怎么放都只有一种放法(允许空箱)
dp[1][j] = 1; 将1个球放进j个箱子中,不管怎么放都只有一种放法
dp[i][1] = 1; 将i个球放进1个箱子中,不管怎么放都只有一种放法
dp:
当n < m时 dp[i][j] = dp[i][j-1]; 假设将2个球放进3个箱子中和将2个球放进4个箱子中,他们的放法一致
当n >= m时 dp[i][j] = dp[i][j-1] + dp[i-j][j];
其中dp[i][j-1]代表有空箱的情况存在,他的状态可以由空一个箱子转移
dp[i-j][j]代表不空箱的情况,那么至少每个箱子中都要放一个球,那么还剩下i-j个球,然后就变成了i-j个球放进j个箱子中的子问题
代码
void init() { for(int i = 0; i < maxn; i++){ for(int j = 0; j < maxn; j++){ if(i == 0 || i == 1){ dp[i][j] = 1; } if(j == 1){ dp[i][j] = 1; } } } } void solve(){ init(); for(int i = 2; i < maxn; i++){ for(int j = 1; j < maxn; j++){ if(i < j){ dp[i][j] = dp[i][j-1]; } else{ dp[i][j] = dp[i][j-1] + dp[i-j][j]; } } } } int main(){ solve(); cin>>n>>m; int p=n/m; __int128 ans=1; for(int i=2;i<=p;i++){ int z=n-i*m; if(z==0){ ans++; continue; } ans+=dp[z][i]; } out(ans); }