一、 问题 现在有一正整数N,要把它分为若干正整数之和,问有多少种本质不同的分法?

(1)其中最大数不超过m, 有多少种分法?

(2)分割后的正整数的数目不超过m个, 有多少种分法?

(3)分成最大数不超过m, 且每一个正整数都是正奇数, 有多少种分法?

(4)分成最大数不超过m, 且每一个正整数都不同,有多少种分法?

(5)分成恰好k个正整数,有多少种分法?

 

二、分析

(1)最大数不超过 m

  (a)设dp[i][j]表示把数字 i 分成最大数不超过  j 的若干正整数之和所得的方法数

  (b)有如下递推公式 ,核心是最大数m到底选还是不选

整数划分问题(记忆化搜索和DP方法)

(2)分割数不超过m个

  (a)设dp[i][j] 表示把数字 i 分成分割数不超过 j 的若干正整数之和所得的方法数

  (b)递推公式核心

  • 到底分不分成 j 个
    •   如果分成j个,则预先给每一份分 一个1,那么还剩下n-m,这n-m仍然继续分割成最多j个数;
    •   如果不分成j个,那就转移到了把 i 最多分成 j-1 个数的状态
  •  递推公式:

整数划分问题(记忆化搜索和DP方法)

  • 发现竟然和(1)完全一样!其实(1)(2)问题是完全等价的
    •   从递推公式上来看,等价
    •   从图形来看等价:

整数划分问题(记忆化搜索和DP方法)

 

(3)分成最大数不超过m, 且每一个数都是正奇数

  (a)设dp[i][j] 表示把数字 i 分成分割数不超过 j 的若干正奇数之和所得的方法数

  (b)递推公式核心: 最大数 j 到底是不是奇数

  • 如果是奇数,那么 j 是可以作为 一种分法的元素的 转态转移到到底是分出 j 还是不分出 j
  • 如果不是奇数, 那么 j 是不能分出来的,状态转移到了dp[i][j-1]

整数划分问题(记忆化搜索和DP方法)

 

(4)分成最大数不超过m, 且每一个正整数都不同

  (a)设dp[i][j] 表示把数字 i 分成最大数不超过 j 的若干不同正整数之和所得的方法数

  (b)递推公式核心:当前最大数选还是不选,如果选,还剩下i-j并且下次的最大数不能再是j而是j-1, 如果不选,状态转移到dp[i][j-1]

整数划分问题(记忆化搜索和DP方法)

 

 

 (5)分成恰好k个正整数

  (a)设dp[i][j] 表示把数字 i 分成 j 个正整数之和所得的方法数

  (b)递推公式核心

  • 这j个数里面含不含有1
    •   若不含1,则先为每份预分配一个1,再对i-j进行分割成j份;
    •   若含1,则先分出一个1, 然后再对剩下的的i-1分成 j-1份

整数划分问题(记忆化搜索和DP方法)

【记忆化搜索代码】

#include<iostream>
#include<queue>
#include<list>
#include<vector>
#include<cstring>
#include<set>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<cmath>
#include<algorithm>
#include<string>
#include<stdio.h>
using namespace std;
typedef long long ll;
const double eps=1e-8;
const double PI = acos(-1.0);
const int inf = 0x3f3f3f3f;
const ll INF = 0x7fffffff;
#define MS(x,i) memset(x,i,sizeof(x))
#define rep(i,s,e) for(int i=s; i<=e; i++)
#define sc(a) scanf("%d",&a)
#define scl(a) scanf("%lld",&a)
#define sc2(a,b) scanf("%d %d", &a, &b)
#define dubug printf("debug......\n");
const int maxn = 1e2+10;
int dx[4] = {0, 0, 1, -1};
int dy[4]  = {1, -1, 0 , 0};


/*
1. 把n划分成若干正整数之和 最大数不超过m 方法数
2. 把n划分成不超过m个正整数之和 方法数
1.2 等价
*/
ll dp1[maxn][maxn];
ll dp2[maxn][maxn];
ll dp3[maxn][maxn];
ll dp4[maxn][maxn];

//把n划分成不超过m的若干正整数之和     把n划分成不超过m个正整数之和 方法数
ll DP1(int n, int m){
    if(dp1[n][m] != -1){
        return dp1[n][m];
    }
    ll ans = 0;
    if(m == 1 || n == 1){
        return dp1[n][m] = 1;
    }
    if(m == n){
        return dp1[n][m] = DP1(n , m - 1) + 1;
    }
    if(m > n){
        return dp1[n][m] = DP1(n,n);
    }
    if(m < n){
        return dp1[n][m] = DP1(n-m, m) + DP1(n , m-1);
    }
    return dp1[n][m] = 0;
}

//把N分成不超过m的,若干个正奇数的和 的方法数
ll DP2(int n , int m){
    if(dp2[n][m] != -1) return dp2[n][m];

    if(n == 1 || m == 1){
        return dp2[n][m] = 1;
    } 
    if(n == m){
        return dp2[n][m] = DP2(n , m-1) + m%2;
    }
    if(n < m){
        return dp2[n][m] = DP2(n , n);
    }
    if(n > m){
        if(m % 2){
            return dp2[n][m] = DP2(n-m , m) + DP2(n , m-1);
        }
        else{
            return dp2[n][m] = DP2(n , m - 1);
        }
    }
    return dp2[n][m] = 0;
}

//把N划分成不超过m的 不同正整数之和 的方法数
ll DP3(int n, int m){
    if(dp3[n][m] != -1){
        return dp3[n][m];
    }
    if(n == 1) return dp3[n][m] = 1;
    if(m == 1 && n > 1){
        return dp3[n][m] = 0;
    }

    if(m == n){
        return dp3[n][m] = DP3(n , m - 1) + 1;
    }
    if(m > n){
        return dp3[n][m] = DP3(n,n);
    }
    if(m < n){
        return dp3[n][m] = DP3(n-m, m-1) + DP3(n , m-1);
    }
    return dp3[n][m] = 0;
}

//把n换分成恰好k个正整数之和
ll DP4(int n, int k){
    if(dp4[n][k] != -1) return dp4[n][k];
    if(n == 1 && k > 1) return dp4[n][k] = 0;
    if(k == 1) return dp4[n][k] = 1;
    if(n == k) return dp4[n][k] = 1;
    if(n < k) return dp4[n][k] = 0;
    if(n > k) return dp4[n][k] = DP4(n-k , k) + DP4(n-1, k-1);
    return 0;
}


int n,m;
int k;
int main(){
    while(sc(n) != EOF){
        MS(dp1, -1);
        MS(dp2 , -1);
        MS(dp3 , -1);
        MS(dp4 , -1);
        sc(k);
        //printf("%lld\n", DP1(n,n));
        printf("%lld\n", DP4(n,k));
       // printf("%lld\n", DP1(n,k));
        printf("%lld\n", DP3(n,n));
         printf("%lld\n", DP2(n,n));
        
    }

    return 0;
}
View Code

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-06-20
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-02-24
猜你喜欢
  • 2021-07-11
  • 2021-11-04
  • 2021-12-24
  • 2022-12-23
  • 2021-08-12
  • 2022-01-21
  • 2022-12-23
相关资源
相似解决方案