(01背包型动态规划+三维状态数组)lintcode困难89 · K数之和

题目

描述
给定 n 个不同的正整数,整数 k(k \leq n)(k≤n)以及一个目标数字 target。 
在这 n 个数里面找出 k 个数,使得这 k 个数的和等于目标数字,求问有多少种方案?

样例

样例 1:
输入:
A = [1,2,3,4]
k = 2
target = 5
输出:
2
解释:
1 + 4 = 2 + 3 = 5

样例 2:
输入:
A = [1,2,3,4,5]
k = 3
target = 6
输出:
1
解释:
只有这一种方案。 1 + 2 + 3 = 6

分析

解读题意,给定一个数组A,两个变量k和target,要求从数组A中拿出k个数字他们之和为target,求有多少种方案,每个数字只能拿一次。
判断是一道01背包型动态规划的题,但是多了一个限制条件k,如果没有k的情况下,我们直接就创建一个二维数组,一个代表当前的物品,一个代表背包的最大承重,这里只需要把k加入到状态中即可,还是老方法,先分析最后一步,然后分析转移方程。

1.最后一步

最后一个物品拿还是不拿
情况一不拿
如果最后一个物品不拿,当前方案数最大就是前n-1个物品选k个的最大方案数
情况二拿
最大方案数就是就是前n-1个物品选k-1个的最大方案数
注意选当前物品时背包的最大承重要大于当前的物品(就是说背包可以装的下当前的物品)

2.转移方程

状态f[i][j][k],意思是前i个物品中选前j个,总重量达到k的最大方案数
不拿当前物品时f[i][j][k]=f[i-1][j][k]
拿当前物品时f[i][j][k]=f[i-1][j-1][k-A[i-1]]
拿或者不拿是两个相对的状态,所以总的状态应该将这两种状态加起来
f[i][j][k]=f[i-1][j][k]+f[i-1][j-1][k-A[i-1]]|(j-1>=0&&k>=A[i-1])

代码部分

1.初始化

状态需要0的情况,就是在前0个数字中选…

   	int len=A.size();
    	//前len个数字中选k个和为target,方案数量最多 
    	vector<vector<vector<int> > > f(len+1,
		vector<vector<int> >(k+1,vector<int>(target+1)));

转移方程中,我们需要上一行的数据,所以正常来说我们应该初始化第一行
初始化f[0][0][0],在前0个数字中选0个使总重量为0,这种情况我们什么都不用做就满足,所以这种情况下我们应该初始化为1
当前0个数字的情况,不论是拿1个2个,还是想使总重量达到几,都是达不到的,所以我们应该初始化f[0][0j][0k]的所有值为0

    	f[0][0][0]=1;
    	
    	//初始化前0个物品的情况 
    	for(int i=0;i<=k;i++)			
    		for(int j=0;j<=target;j++)
    		{
    			if(i!=0||j!=0)
    				f[0][i][j]=0;
			}
2.动规核心

按顺序计算[1][0][0]…[1][0][k]…[1][j][k]
[i][0][0]…[i][0][k]…[i][j][k]
最后结果就是f[i][j][k]
要注意对下标进行判断,考虑所有可能出现的数组越界访问情况

   	for(int i=1;i<=len;i++)		//动规核心 
    	{
    		for(int j=0;j<=k;j++)
    		{
    			for(int l=0;l<=target;l++)
    			{
    				f[i][j][l]=f[i-1][j][l];
    				if(l>=A[i-1]&&j-1>=0)
    					f[i][j][l]+=f[i-1][j-1][l-A[i-1]];
				}
			}
		}

完整代码

class Solution {
public:
	//选最后一个物品:f[i-1][k-1][target-A[i-1]] 
	//不选最后一个物品:f[i-1][k][target]
    int kSum(vector<int> &A, int k, int target) {
    	int len=A.size();
    	//前len个数字中选k个和为target,方案数量最多 
    	vector<vector<vector<int> > > f(len+1,
		vector<vector<int> >(k+1,vector<int>(target+1)));
    	
    	f[0][0][0]=1;
    	
    	//初始化前0个物品的情况 
    	for(int i=0;i<=k;i++)			
    		for(int j=0;j<=target;j++)
    		{
    			if(i!=0||j!=0)
    				f[0][i][j]=0;
			}
    			
    	for(int i=1;i<=len;i++)		//动规核心 
    	{
    		for(int j=0;j<=k;j++)
    		{
    			for(int l=0;l<=target;l++)
    			{
    				f[i][j][l]=f[i-1][j][l];
    				if(l>=A[i-1]&&j-1>=0)
    					f[i][j][l]+=f[i-1][j-1][l-A[i-1]];
				}
			}
		}
		
		return f[len][k][target];
    }
};

总结

01背包问题,多了一个限制的条件,其他不变,我们还是按照正常的思路分析最后一步,然后分析转移方程,唯一不同的是,需要把这个变量代入到我们的状态中。

你可能感兴趣的:(算法,动态规划)