【编程测试题】数字和为sum的方法数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/HelloZEX/article/details/81902666

题目描述

给定一个有n个正整数的数组A和一个整数sum,求选择数组A中部分数字和为sum的方案数。
当两种选取方案有一个数字的下标不一样,我们就认为是不同的组成方案。

输入描述:

输入为两行:
 第一行为两个正整数n(1 ≤ n ≤ 1000),sum(1 ≤ sum ≤ 1000)
 第二行为n个正整数A[i](32位整数),以空格隔开。

输出描述:

输出所求的方案数

// 刚开始是用递归写的,说实话递归即好想有好写。但是毕竟时间复杂度是O(n^n),
// 运行完后50%超时。然后想到了动态规划。但是吧,不知道怎样规划这个很恼人,
// 最后在评论区找到思路然后开始写代码。
//------------------------------------------------------------------------------------------------------
// 我这里的思路以示例为例,然后我把图标放在了下面,纵坐标除了第一个外是输
// 入;横坐标是从0-sum的所有值。数组内格子含义是该行最左侧数字(即输入)
// 与之前的数字能得到该列数字的最多的可能性:

// 然后说一下做法:
// 1.由于每个数总能把0填上,且0不可填上初0外其余数,所以数组第一行全填0,第
// 一列全填1;
// 2.从第二行第二列开始遍历数组。如果列所在数字减去该行数小于0,那么该格子继
// 承本列上一行的数字。例如图中(2,10)对应格子。由于让10得到2,那必须由-8+5得
// 到,但是该题无法得到比0小的数,因此由10之前的数得到2的最多可能与他之前的
// 数(即5)是一样的;
// 3.如果列所在数字减去该行数大于等于0,该格子内容为该列上一行数字与上一行差
// 值所在格子数量和。什么意思呢?例如(10,3),若想要用3之前的数列得到10,除了
// 它上一行(即2)本身就能得到2个10外,只要之前的数字是7,7+3依然可以得到10。因
// 此去看上一行中列数为7的格子数值,为2,即它上一个数有2中组合得到7,7+3=10。
// 那该行数值即为2+2=4。
// 代码如下:
#include <iostream>
#include <vector>
using namespace std;
int main(void)
{
    int n, sum;
    cin >> n >> sum;
    vector<long> tmp(sum + 1, 0), input(n + 1, 0);
    tmp[0] = 1, input[0] = 0;
    vector<vector<long> >dp(n + 1, tmp);
    for (int i = 1; i <= n; i++) cin >> input[i];
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= sum; j++)
        {
            if (j - input[i] >= 0) dp[i][j] = dp[i - 1][j] + dp[i - 1][j - input[i]]; //这个就如解析力写的一样
            else dp[i][j] = dp[i - 1][j]; //否则直接继承该列上一行值
        }
    }
    cout << dp[n][sum] << endl;  //输出最后一个
    return 0;
}

有点看不懂动态规划的就很烦!!~~


用动态规划,类似01背包问题,f(i , j )表示前i 个数中和为 j 的方案数, 则 若 j >= a[i],  f ( i ,j) = f(i -1, j)+ f (i - 1,j - a[i] );

否则,  f ( i ,j) = f(i -1, j)。

可优化地方:由于二维数组中,第i行 只与第 i - 1 行有关,所有我们若从 最后一列 开始更新数组,则可用一维数组来保存先前状态。

时间复杂度为:O( n * sum ) 。

#include<iostream>
#include<vector>
using namespace std;
 
int main() {
    int n, sum;
    cin>>n>>sum;
    
    vector<long long> a(sum+1);
    vector<int> b(n);
    
    for(int i=0; i<n; i++)
        cin>>b[i];
    
    a[0] = 1;
    
    for (int i=0; i<n; i++)
        for (int j=sum; j>=b[i]; j--)
          	a[j] += a[j-b[i]];
    
    
    cout<<a[sum]<<endl;
    
    return 0;
    
}


猜你喜欢

转载自blog.csdn.net/HelloZEX/article/details/81902666