zoukankan      html  css  js  c++  java
  • dp算法

    题目

    总时间限制: 200ms 内存限制: 65536kB
    描述
    将正整数n 表示成一系列正整数之和,n=n1+n2+…+nk, 其中n1>=n2>=…>=nk>=1 ,k>=1 。
    正整数n 的这种表示称为正整数n 的划分。

    输入
    标准的输入包含若干组测试数据。每组测试数据是一行输入数据,包括两个整数N 和 K。
    (0 < N <= 50, 0 < K <= N)
    输出
    对于每组测试数据,输出以下三行数据:
    第一行: N划分成K个正整数之和的划分数目
    第二行: N划分成若干个不同正整数之和的划分数目
    第三行: N划分成若干个奇正整数之和的划分数目
    样例输入
    5 2
    样例输出
    2
    3
    3
    提示
    第一行: 4+1, 3+2,
    第二行: 5,4+1,3+2
    第三行: 5,1+1+3, 1+1+1+1+1+1

    第一问

    这个问题比之前的整数划分问题多了一个限制条件K,可以把这个问题类比为背包问题,问题转化为如下:
    从[1...N]中选择K个数,其和为N,并且选数可以重复,这是个典型的动态规划问题。
    那么考虑子问题,从[1...i]中选q个数,其和为j.
    令f[i][j][q] 表示子问题的解。
    下面考虑边界条件:从[1...1]中选择1 个数,其和为1,选法唯一 f[1][1][1] = 1;
    接下来考虑状态转移:
    f[i][j][q] = f[i-1][j][q] + f[i][j - i][q - 1]
    f[i-1][j][q]表示第i个数不选,那么要从前i-1个数中选q个,其和为j
    f[i][j - i][q-1]表示第i个数选,那么从前i个数中选q-1个,其和为j-i,这里i不取i-1是因为在子问题中还会用到i,即每个数选择不唯一,
    这是区别于一般背包问题的特殊情况。
    这样,解这道题使用三位数组来记录动态规划过程就可以了,但是有些动态规划是可以使用滚动数组来节省空间,这道就可以。
    那么假设一个二位数组f[j][q],用一层循环表示从前i个数中选择,在第i次循环开始执行前,f[j][q]表示从前i-1个数中选q个凑成j,对于f[i][j - i][q-1]
    因为在f[i][j][q]之前遍历到,所以f[j-i][q-1]中的值是第i次循环更新过的值,这样一来,状态转移就可以简化为:
    f[j][q] += f[j-i][q-1]
    这部分代码如下:

    dp[0][0] = 1;
    for (int i = 1; i <= n; i++)
        for (int j = i; j <= n; j++)
    	for (int q = k; q >= 1; q--)
    	    dp[j][q] += dp[j - i][q - 1];
    printf("%d
    ", dp[n][k]);
    

    第二问

    第二问比之前简单的整数划分问题增加了数字不能重复的限制,这表现在状态转移方程里边就是:
    设f[i][j]表示从前i个数中凑j,
    状态转移方程为:
    f[i][j] = f[i-1][j-i] + f[i-1][j]
    同样,可以用滚动数组将二维转化为一维,注意j要从大到小遍历
    代码:

    dp1[0] = 1;
            for (int i = 1; i <= n; i++)
                for (int j = n; j >= i; j--)
                    dp1[j] += dp1[j - i];
            printf("%d
    ", dp1[n]);
    

    第三问

    第三问比简单的整数划分问题增加了只能选择正奇数,注意,数字还是可以重复的,这个其实在弄懂了前两个题之后就很好写了
    直接写出状态转移方程:
    f[i][j] = f[i][j - i] + f[i-2][j]
    f[1][1] = 1
    用滚动数组的话,i取奇数遍历,j需要从小到大遍历。
    代码:

    dp2[0] = 1;
            for (int i = 1; i <= n; i += 2)
                for (int j = i; j <= n; j++)
                    dp2[j] += dp2[j - i];
            printf("%d
    ", dp2[n]);
    

    整道题代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    using namespace std;
    
    typedef long long ll;
    
    const int N = 50 + 5;
    int dp[N][N], dp1[N], dp2[N];
    
    int main() {
        for (int n, k; scanf("%d%d", &n, &k) == 2;) {
            memset(dp, 0, sizeof(dp));
            memset(dp1, 0, sizeof(dp1));
            memset(dp2, 0, sizeof(dp2));
            dp[0][0] = 1;
            for (int i = 1; i <= n; i++)
                for (int j = i; j <= n; j++)
                    for (int q = k; q >= 1; q--)
                        dp[j][q] += dp[j - i][q - 1];
            printf("%d
    ", dp[n][k]);
            dp1[0] = 1;
            for (int i = 1; i <= n; i++)
                for (int j = n; j >= i; j--)
                    dp1[j] += dp1[j - i];
            printf("%d
    ", dp1[n]);
            dp2[0] = 1;
            for (int i = 1; i <= n; i += 2)
                for (int j = i; j <= n; j++)
                    dp2[j] += dp2[j - i];
            printf("%d
    ", dp2[n]);
        }
        return 0;
    }
    

    代码参考:https://blog.csdn.net/Nightmare_ak/article/details/94414363

  • 相关阅读:
    【URL重写】IIS7配置URL重写
    【IIS7.5】Asp文件上传限制,加载页面大小限制
    msxml3.dll 错误 '800c0005' 系统错误: -2146697211。
    【转】修改3389远程端口的批处理文件.bat
    第一篇:无角牛MVC通用后台数据库设计
    无角牛MVC通用后台
    个人收集资料整理-WebForm
    个人收集资料整理-WinForm
    win7系统中桌面图标显示不正常问题
    ASP.NET MVC 第六回 过滤器Filter
  • 原文地址:https://www.cnblogs.com/zhangyue123/p/12557503.html
Copyright © 2011-2022 走看看