• 13.整数划分 计数类DP


     每种划分方式都是按照从大到小排序的

    样例分析:

    5有7种划分方式

    5 = 5

    5 = 4 + 1

    5 = 3 + 2

    5 = 3 + 1 + 1

    5 = 2 + 2 + 1

    5 = 2 + 1 + 1 + 1

    5 = 1 + 1 + 1 + 1 + 1

    解法一:用背包问题的思想

    把整数n看做一个容量为n的背包

    然后一共有n个物品,每个物品的体积分别是1,2,3,...,n。(也就是n这个数的所有因子)

    每种物品可以用无限次,所以是完全背包问题

    求恰好装满背包的方案数

    状态表示:

    集合:dp[i][j]表示从物品1~i中选,总体积恰好为j的选法的集合

    属性:数量

    状态计算:集合的划分,同背包问题

    根据第i个物品选了几个来划分

    状态数量:n ^ 2

    转移数量:n(粗略估计的,比n小,应该为log n)

    时间复杂度:n ^ 3

    然后进行优化

     

    二维做法

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int N = 1010, mod = 1e9 + 7;
     4 int dp[N][N];
     5 int main() {
     6     int n;
     7     cin >> n;
     8     for (int i = 0; i <= n; i++) { //注意是从0开始
     9         dp[i][0] = 1; //容量为0时,前i个物品全不选也是一种方案
    10     }
    11     for (int i = 1; i <= n; i++) {
    12         for (int j = 0; j <= n; j ++) {
    13             dp[i][j] = dp[i - 1][j] % mod; //特殊dp[0][0] = 1
    14             if (j >= i) {
    15                 dp[i][j] = (dp[i - 1][j] + dp[i][j - i]) % mod;
    16             }
    17         }
    18     }
    19     cout << dp[n][n] << endl;
    20     return 0;
    21 }

    然后再转化为一维,再从小到大循环体积

    注意初值问题:
    求最大值时,当都不选时,价值显然是 0
    而求方案数时,当都不选时,方案数是 1(即前 i 个物品都不选的情况也是一种方案),所以需要初始化为 1
    即需要这样:

    for  (int i = 0; i <= n; i++)  {

      dp[i][0] = 1;

    }
    等价变形后: f[0] = 1

    一维做法

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int N = 1010, mod = 1e9 + 7;
     4 int dp[N];
     5 int main() {
     6     int n;
     7     cin >> n;
     8     dp[0] = 1; //一个数都不选,是一种方案
     9     for (int i = 1; i <= n; i++) {
    10         for (int j = i; j <= n; j++) {
    11             dp[j] = (dp[j] + dp[j - i]) % mod;
    12         }
    13     }
    14     cout << dp[n] << endl;
    15     return 0;
    16 }

     解法二:

     PS : 最小值1 表示 :j个数的总和是i的数中最小的数为1

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int N = 1010, mod = 1e9 + 7;
     4 int dp[N][N];
     5 int main() {
     6     int n;
     7     cin >> n;
     8     dp[0][0] = 1; //总和是0,用0个数来表示,是1种方案
     9     for (int i = 1; i <= n; i++) {
    10         for (int j = 1; j <= i; j++) { //总和是i的话,最多表示成i个数的和
    11             dp[i][j] = (dp[i - 1][j - 1] + dp[i - j][j]) % mod;  
    12         }
    13     }
    14     int res = 0;
    15     for (int i = 1; i <= n; i++) {
    16         res = (res + dp[n][i]) % mod;
    17     }
    18     cout << res << endl;
    19     return 0;
    20 }
  • 相关阅读:
    RabbitMQ学习之:(一)初识、概念及心得
    给Eclipse_Android添加智能提示功能
    C#异步编程之:(三)使用TaskScheduler.UnobservedTaskException
    类设计技巧
    初始化块
    子类构造器
    静态域与静态方法
    构造器中调用另一个构造器
    重载
    无参数的构造器
  • 原文地址:https://www.cnblogs.com/fx1998/p/13235646.html
Copyright © 2020-2023  润新知