信息学奥赛一本通 1304:数的划分

时间限制: 1000 ms         内存限制: 65536 KB

【题目描述】

将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。

例如:n=7,k=3,下面三种分法被认为是相同的。

1,1,5; 1,5,1; 5,1,1

问有多少种不同的分法。 输出一个整数,即不同的分法。

【输入】

两个整数n,k(6<n≤200,2≤k≤6),中间用单个空格隔开。

【输出】

一个整数,即不同的分法。

【输入样例】

7 3

【输出样例】

4

【分析】

1、递归:递归函数每次将整数n分出一个数i,剩下的n-i进入下一次递归。

    为了避免出现相同方案,每次分出的数不比上一个分出的数大,即分出的数按升序排列。
2、动规:

    题目转换为更加形象具体的叙述:有n个苹果需要划分到k个盘子里面,不允许有空盘,不考虑顺序。

    dp[i][j]表示将i个苹果放到j个盘子里的不同分配方法的个数。

    将n个苹果放入k个盘子,首先要保证k个盘子不为空,即默认每个盘子初始有1个苹果,那么就需要划分剩下的n-k个苹果。
    分为以下情况:
    (1)n-k个苹果都放到k个盘子里,dp[n-k][k];
    (2)n-k个苹果都放到k-1个盘子里,dp[n-k][k-1];
    ......
    (k)n-k个苹果都放到1个盘子里,dp[n-k][1]。
    所以,可以得出通式 dp[n][k] = dp[n-k][k] + dp[n-k][k-1] + dp[n-k][k-2] + ... + dp[n-k][1]。
    但是,根据通式又可以推出, dp[n-1][k-1] = dp[n-k][k-1] + dp[n-k][k-2] + ... + dp[n-k][1]。
    两式结合,最终可以得出,dp[n][k] = dp[n-k][k] + dp[n-1][k-1],即状态转移方程!
    根据dp[i][j]的含义,显然 dp[1][j] = 1,dp[i][1] = 1。(j <= i)
    
    上述的推导过程简单来说就是:
    将n个苹果放到k个盘子中的情况总数 = 至少有一个盒子只有一个小球的情况数 + 没有一个盒子只有一个小球的情况数
    (1)至少有一个盒子只有一个小球,因为盒子不加区分,那么情况数与“将n-1个小球放到k-1个盒子中”的情况数一样;
    (2)没有一个盒子只有一个小球,那么把每个盒子中拿出来一个小球,对应的是“把(n-k)个小球放到k个盒子中的情况数”。

#include<bits/stdc++.h>
using namespace std;

//【递归】
int n, k;
int sum;

void f(int n, int k, int start)
{
	if(n == 0 || k == 0)
	{
		if(n == 0 && k == 0)
		{
			sum++;
		}
		return ;
	}
	for(int i = start; i <= n; i++)
	{
		if(n - i < 0)
		{
			return ;
		}
		f(n - i, k - 1, i);
	}
}

int main()
{
	cin >> n >> k;
	f(n, k, 1);
	cout << sum;
}

(动态规划中强大的逻辑思维能力真的很重要,看着大佬的代码研究了半天......)

#include<bits/stdc++.h>
using namespace std;

//【动规】
int dp[205][10];
int n, k;

int main()
{
	cin >> n >> k;
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= k && j <= i; j++)
		{
			if(i == 1 || j == 1)
			{
				dp[i][j] = 1;
			}
			else
			{
				dp[i][j] = dp[i - j][j] + dp[i - 1][j - 1];
			}
		}
	}
	cout << dp[n][k];
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值