动态规划深度搜索数的划分

问题描述

一个正整数可以划分为多个正整数的和,比如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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值