问题描述
一个正整数可以划分为多个正整数的和,比如n=6时: 6;1+5;2+4;3+3;2+2+2;1+1+4;1+2+3;1+1+1+3;1+1+2+2;1+1+1+1+2;1+1+1+1+1+1 共有十一种划分方法。 给出一个正整数,问有多少种划分方法。
输入格式
一个正整数n
输出格式
一个正整数,表示划分方案数
样例输入
3
样例输出
3
数据规模与约定
n<=100
思路
正整数n可以看成n个小球,每一个小球代表数字一,观察可知划分即为将这个n个小球放入k的盒子里面,且每一个盒子不能为空,k依次为从1到n,k=1时即为n个1相加。可用动态规划的思路,每一次分完之后,**(1)**如果k个盒子里面存在有的盒子小球数量为1,则和将n-1个小球放入k-1个盒子的方法是一样的,即dp(i)(j) = dp(i-1)(j-1);(dp(i)(j))表示i个小球放入j个盒子的方法 (2)如果盒子中不存在只有一个小球的情况,那么每一个盒子至少有一个小球,假设从每一个盒子中拿出一个小球,那么方法也不会变,所以dp(i)(j)=dp(i-j)(j),得出转移状态方程为dp(i)(j)=dp(i-j)(j)+dp(i-1)(j-1)
#include <iostream>
using namespace std;
int dp[201][7];
int main(void)
{
int i , j ;
int n;
int sum = 0;
dp[0][0] = 1;
//注意初始化的问题,只有当i>=j时条件才可以成立,当i=j时方法为1,且dp[i-j]
[j]=0,
//那么dp[i-1][j-1]到最后就为1,所以将dp[0][0]设为1
cin >> n;
for (i = 1; i <= n; i++)
{
for (j = 1; j <= n; j++)
{
if (i >= j)
{
dp[i][j] = dp[i - 1][j - 1] + dp[i - j][j];
}
}
}
for (i = 1; i <= n; i++)
sum += dp[n][i];//dp[n][i]表示将n个小球放入i(1-n)个盒子里面,加起来就是最后的总和
cout << sum;
return 0;
}
深度搜索算法(用来做此题目会超时超时)打印出所有情况
#include <iostream>
using namespace std;
typedef long long ll;
ll n;
ll sum = 0;
ll num;
int arr[300];
int brr[300];
void dfs(int step,int k)//step表示站在第几个盒子面前开始处理,k表示盒子的数目,
{
int i;
num = 0;
if (step == k + 1)//此时所有盒子已经处理完判断是否与n相等,是则输出来
{
for (i = 1; i <= k; i++)
{
num += arr[i];
}
if (num == n)
{
printf("%lld=%d",n,arr[1]);
for (i = 2; i <= k; i++)
printf("+%d", arr[i]);
cout << endl;
sum++;
}
return;
}
for (i = 1; i <= n; i++)
{
arr[step] = i;
if (arr[step] >= arr[step - 1])
{//因为1 2 3和3 2 1是同一种情况,所以让后一个数小于等于前一个数防止重复
dfs(step + 1,k);
}
}
return;
}
int main(void)
{
cin >> n;
for (int i = 1; i <= n; i++)
{
dfs(1, i);
}
cout << sum;
return 0;
}
深度搜索剪枝
#include <iostream>
using namespace std;
int arr[1000];
int n;
int ans = 0;
int sum;//sum记录剩余数字的和
void dfs(int step, int sum,int k)
{
//arr[step]还没有填,如果arr[step]==k,表明仅有一个arr[step]的盒子还没有填,此时如果sum>=arr[step-1]
//保证了数列从小到大的顺序,那么arr[step]必定有解且唯一,不需要尝试arr[step]
//例:n = 10,k = 4: 1 2 3 4是一组解,当step==k,即arr[1] = 1,arr[2] = 2,arr[3] = 3,arr[4]未知,此时sum = 4>arr[step-1],
//那么arr[step]必定为4 ,不需要尝试arr[step]的数字,从而减少了搜索次数
if (step == k)
{
if (sum >= arr[step-1])
ans++;
if (k == 1)
cout << n << '=' << n << endl;
else {
cout << n << '=' << arr[1];
for (int i = 2; i <= step - 1; i++)
cout << '+' << arr[i];
cout << '+' << sum << endl;
}
return;
}
//此方法没有上面的优化
//if (step-1 == k)
//{ arr[step]还没有填,如果step-1==k,并且sum=0,表明全部盒子填完且符合情况,输出
// if (sum == 0)
// {
// ans++;
// cout << n << '=' << arr[1];
// for (int i = 2; i <= step-1; i++)
// cout << '+' << arr[i];
// cout << endl;
// }
// return;
//}
for (int i = arr[step - 1]; i <= sum / (k - step + 1); i++)//上下界剪枝
{
arr[step] = i;
//保证前面的数字小于后面的数字,每次从step-1开始枚举,且保证枚举的数字不能超过后面总和的平均数
//例如1 2 3 4,为一组解,那么在枚举arr[3]的时候,此时sum = 7,sum/(k-step+1)=3.5,则arr[3]不能超过3,否则arr[4]必定比arr[3]要小
//不满足前面的数字比后面的数字大,造成重复计算
dfs(step + 1, sum - i, k);//尝试arr[step]=i后,sum-i;
}
}
int main(void)
{
cin >> n;
arr[0] = 1;
for (int i = 1; i <= n; i++)
{
dfs(1, n, i);
}
cout << ans;
return 0;
}