原题网址:https://www.lintcode.com/problem/k-sum/description

描述

给定n个不同的正整数,整数k(k < = n)以及一个目标数字。 

在这n个数里面找出K个数,使得这K个数的和等于目标数字,求问有多少种方案?

您在真实的面试中是否遇到过这个题?  是

样例

给出[1,2,3,4],k=, target=,[1,4] and [2,3]是个符合要求的方案

标签
LintCode 版权所有
动态规划(DP)
 
 
思路:最开始参照了k数和Ⅱ用递归来做,结果只通过18%的数据就Time Limit Exceeded了。点开标签一看,要用动态规划……一口老血。
好吧,动态规划就动态规划。刚做过背包问题,背包问题中用背包容量作为动态数组一个维度,另一个维度是取0~i个物体,dp值是当前最大容量。而这道题,目标数字相当于背包容量,要作为一个维度,但与背包问题不同的是,这道题是从前 i 个中取出若干个(j),又多出一个维度,所以要用三维动态数组,dp值表示当前方案数,大爷的……
 
也就是说,dp【i】【j】【t】表示从前i个数中取出j个,这些数的和要等于t,有多少种方案。
 
状态转移方程:dp【i】【j】【k】=dp【i-1】【j】【t】+dp【i-1】【j-1】【t-A【i】】(当然前提是t>=A【i】);
                    dp[i][j][t]=dp[i-][j][t];
if (t>=A[i])//注意是大于等于;
{
dp[i][j][t]+=dp[i-][j-][t-A[i]];
}

意思就是,每个元素都有两种状态,取或者不取:

(1)若不取A[i]这个值,当前方案数等于从前i-1个数中取j个使其和为t的方案数,即dp[i - 1][j][t]。

(2)若取当前值A[i],则当前方案数等于从前i-1个数中取j个使其和为t的方案数再加上考虑A[i]的情况,即dp[i - 1][j - 1][t - A[i]](前提是t - A[i]要大于等于0)。

值得注意的是,如果j为0并且t也为0,则dp【i】【j】【t】=1,即是说从任意集合里拿出0个数使其和为0,这种情况只有一种方案,就是不取任何数。
 
 
AC代码:(注意代码实现的时候对i做了处理,dp中的i对应A中的i-1,即dp中是个数,A中是下标。或者也可以单独处理i=0的情况,即如果i==0,j==1,t==A【0】,dp【i】【j】【t】=1,这样可能麻烦一些)
class Solution {
public:
/**
* @param A: An integer array
* @param k: A positive integer (k <= length(A))
* @param target: An integer
* @return: An integer
*/
int kSum(vector<int> &A, int k, int target) {
// write your code here
int size=A.size();
if (target<)
{
return ;
}
vector<vector<vector<int>>> dp(size+,vector<vector<int>>(k+,vector<int>(target+,))); for (int i=;i<=size;i++)
{
for (int j=;j<=k;j++)
{
for (int t=;t<=target;t++)
{
if (j==&&t==)//前i个数中取0个和为0只有一种方案,就是不取任何数;
{
dp[i][j][t]=;
}
else if (!(i==||j==||t==))
{
dp[i][j][t]=dp[i-][j][t];
if (t>=A[i-])//注意是大于等于;
{
dp[i][j][t]+=dp[i-][j-][t-A[i-]];
}
}
}
}
}
return dp[size][k][target]; }
};
空间优化(滚动数组):
 
可以把dp数组最外层去掉,保留两个维度。
因为 D[i][j][t] += D[i - 1][j - 1][t - A[i - 1]](t>=A[i-1]),这个表达式说明D[i][j][t]是把上一级 i 的结果累加过来,这里我们省去了 i 这一级,在D[j][t]这个二维表里就地累加。
可以参考背包问题,背包问题dp数组优化是当前行代替上一行。这里dp是三维数组,省去一维就是当前二维表代替上一个二维表。
同理,为避免覆盖问题,j和t索引要从后向前遍历。
 
 
简单粗暴点修改上面的代码,直接去掉一个dp维度即可,AC代码如下:
class Solution {
public:
/**
* @param A: An integer array
* @param k: A positive integer (k <= length(A))
* @param target: An integer
* @return: An integer
*/
int kSum(vector<int> &A, int k, int target) {
// write your code here
int size=A.size();
if (target<)
{
return ;
}
vector<vector<int>> dp(k+,vector<int>(target+,));
dp[][]=;
for (int i=;i<=size;i++)
{
for (int j=k;j>=;j--)
{
for (int t=target;t>=;t--)
{
if (!(i==||j==||t==))
{
if (t>=A[i-])//注意是大于等于;
{
dp[j][t]+=dp[j-][t-A[i-]];
}
}
}
}
}
return dp[k][target]; }
};
代码还可以进一步优化:
int kSum2(vector<int> &A, int k, int target)
{
int size=A.size();
if (target<)
{
return ;
}
vector<vector<int>> dp(k+,vector<int>(target+,));
dp[][]=; for (int i=;i<=size;i++)//注意此处下标范围与下面A下标对应;
{
for (int j=k;j>;j--)
{
for (int t=target;t>;t--)
{
if (t>=A[i-])//注意是大于等于;
{
dp[j][t]+=dp[j-][t-A[i-]];
}
}
}
}
return dp[k][target];
}

还有更简洁的版本:

class Solution {
public:
/**
* @param A: an integer array.
* @param k: a positive integer (k <= length(A))
* @param target: a integer
* @return an integer
*/
int kSum(vector<int> A, int k, int target) {
// wirte your code here T(n, k, target) = O(n*k*target). area(n, k, target) = O(k*target)
int n = A.size();
int dp[k+][target+];
memset(dp, , sizeof(dp));
dp[][] = ;
for (int x = ; x < n; x++)
for (int y = k; y >= ; y--)
for (int z = target; z >= A[x]; z--)
dp[y][z] += dp[y-][z-A[x]];
return dp[k][target];
}
};

优化成二维dp后,i的遍历可以从0~size-1,也可以从1~size,并不影响结果,就是要注意与A下标对应,前者是A【i】,后者是A【i-1】。因为最初始的那张表是dp【0】【0】=1,其他位置都为0。循环开始后,数组A中每个元素都对应一个二维表,计算过程就是用当前二维表不断代替上一状态的二维表。

 
 
 
PS:dp数组优化前,j和t是从前向后遍历还是从后向前遍历都不影响其结果,因为计算当前i是参照i-1时的数据。 
 
 
一道题做了好久……各种细节出错,不参照答案写不出来,多维动态数组对我而言还是有一定难度的,哭……
 
参考:
lintcode: k Sum 解题报告  讲解详细,启发意义很大
LintCode-k数和  讲解清晰,代码简洁
LintCode -- k数和  代码很简洁
 
 
标记下我最开始的递归代码:
class Solution {
public:
/**
* @param A: An integer array
* @param k: A positive integer (k <= length(A))
* @param target: An integer
* @return: An integer
*/
int kSum(vector<int> &A, int k, int target) {
// write your code here
int result=;
if (A.empty())
{
return result;
}
ksum(A,k,target,,,,result);
return result;
} void ksum(vector<int> &A, int k, int target,int sum,int ind,int size,int &result)
{
if (size==k)
{
if (sum==target)
{
result++;
}
return ;
}
if (ind>=A.size())
{
return ;
}
ksum(A,k,target,sum+A[ind],ind+,size+,result);
ksum(A,k,target,sum,ind+,size,result);
} };

89 k数和的更多相关文章

  1. lintcode 中等题:k Sum ii k数和 II

    题目: k数和 II 给定n个不同的正整数,整数k(1<= k <= n)以及一个目标数字. 在这n个数里面找出K个数,使得这K个数的和等于目标数字,你需要找出所有满足要求的方案. 样例 ...

  2. [LeetCode]Median of Two Sorted Arrays 二分查找两个有序数组的第k数(中位数)

    二分.情况讨论 因为数组有序,所以能够考虑用二分.通过二分剔除掉肯定不是第k位数的区间.如果数组A和B当前处理的下标各自是mid1和mid2.则 1.假设A[mid1]<B[mid2], ①.若 ...

  3. 90 k数和 II

    原题网址:https://www.lintcode.com/problem/k-sum-ii/description 描述 Given n unique integers, number k (1&l ...

  4. K数和问题

    问题描述 给定n个不同的正整数(数组num),整数k(k < = n)以及一个目标数字target.在这n个数里面找出k个数,使得这k个数的和等于目标数字,求问有多少种方案? 解决思路 该类问题 ...

  5. 220. Contains Duplicate III 数组指针差k数值差t

    [抄题]: Given an array of integers, find out whether there are two distinct indices i and j in the arr ...

  6. 陕西师范大学第七届程序设计竞赛网络同步赛D ZQ的睡前故事【约瑟夫环1-N数到第k个出队,输出出队顺序/ STL模拟】

    链接:https://www.nowcoder.com/acm/contest/121/D来源:牛客网 题目描述 ZQ是一个拥有n女朋友的万人迷,她的每一个女朋友每天晚上都会挨个给他打电话,要他讲了睡 ...

  7. lintcode:快乐数

    快乐数 写一个算法来判断一个数是不是"快乐数". 一个数是不是快乐是这么定义的:对于一个正整数,每一次将该数替换为他每个位置上的数字的平方和,然后重复这个过程直到这个数变为1,或是 ...

  8. Project Euler 92:Square digit chains 平方数字链

    题目 Square digit chains A number chain is created by continuously adding the square of the digits in ...

  9. [AGC005D] ~K Perm Counting [dp]

    题面 传送门 思路 首先可以明确的一点是,本题中出现不满足条件的所有的数,都是分组的 只有模$K$意义下相同的数之间才会出现不满足条件的情况,而且仅出现在相邻的情况 那么我们考虑把这个性质利用起来 我 ...

随机推荐

  1. ElasticSearch 增删改查

    HTTP 协议本身语义:GET 获取资源.POST 新建资源(也可以用于更新资源).PUT 更新资源.DELETE 删除资源. ES通过HTTP Restful方式管理数据:1.格式:#操作 /ind ...

  2. 接口测试 java+httpclient+testng+excel

    最近项目不忙,研究了下java实现接口自动化,借助testng+excel实现数据驱动 目前只用post方式测试,返回结果列没有通过列名去找 另外,请求参数是转义之后的,接口之间的依赖也是个问题,批量 ...

  3. if else 和 swith效率比较

    读大话设计模式,开头的毛病代码用if else实现了计算器,说计算机做了三次无用功,优化后是用switch,那么switch为什么比if else效率高呢, 百度找了几个说是底层算法不一样,找了一个比 ...

  4. CSS——用户界面样式

    所谓的界面样式, 就是更改一些用户操作样式, 比如 更改用户的鼠标样式, 表单轮廓等.但是比如滚动条的样式改动受到了很多浏览器的抵制,因此我们就放弃了. 防止表单域拖拽 鼠标样式cursor 设置或检 ...

  5. react 高阶组件之小学版

    高阶组件  多么高大上的概念,一般用来实现组件逻辑的抽象和复用,在很多三方库(redux)中都被使用到,但是开发普通有任务项目时,如果能合理使用高阶组件,也会显著的提高代码质量. 我们今天就用最简单的 ...

  6. Redis-GEO

    一. Redis的GEO特性 Redis3.2版本提供了GEO功能,支持存储地理位置信息用来实现诸如摇一摇,附近位置这类依赖于地理位置信息的功能.二. 命令2.1 增加地理位置信息 命令:geoadd ...

  7. 海量数据解决思路之Hash算法

    海量数据解决思路之Hash算法   一.概述 本文将粗略讲述一下Hash算法的概念特性,里边会结合 分布式系统负载均衡 实例对Hash的一致性做深入探讨.另外,探讨一下Hash算法在海量数据处理方案中 ...

  8. Delphi代码创建形式规范 1.0

                Delphi代码创建形式规范 1.0 本规范的目的:给自己的代码一个统一而标准的外观,增强 可读性,可理解性,可维护性 本规范的原则:名称反映含义,形式反映结构 1.单元风格 ...

  9. PAT甲级——A1104 Sum of Number Segments【20】

    Consider a positive integer N written in standard notation with k+1 digits a​i​​ as a​k​​⋯a​1​​a​0​​ ...

  10. 开发笔记-19/10/28 -SpringBoot @Value 获取配置参数

    1. 在application.properties 定义参数 role.taskEvent :参数名称 4:值 ## ---------------------任务角色--------------- ...