一、寻找和为定值的两个数
算法思想:
- 排序,然后两端扫描。时间复杂度O(nlogn+n) 空间复杂度O(1)
- 散列映射,先确定其中一个数,再判断另一个是否存在。时间复杂度O(1),空间复杂度O(n)
算法实现
#include
#include
#include
using namespace std;
int main()
{
ifstream cin("in.txt");
int n, a[100], ans;
while(cin >> n)
{
for(int i = 0; i < n; ++i)
cin >> a[i];
cin >> ans;
sort(a, a+n);
int i = 0, j = n-1;
while(i < j)
{
if(a[i] + a[j] < ans)
++i;
else if(a[i] + a[j] > ans)
--j;
else
{
cout << a[i] << " " << a[j] << endl;
break;
}
}
}
}
二、扩展:寻找和为定值的任意多个数
1. 递归法
算法思想:考虑是否取第n个数,问题可以转化为前n-1个数和为sum-a[n-1]的问题,也可以转化为后n-1个数的求和问题。使用递归思想解决。
- 如果取第n个数,即求得前n-1个数满足和为sum-a[n-1]的问题
- 如果不取第n个数,即求得前n-1个数满足和为sum的问题
#include
#include
using namespace std;
int res[100], k = 0;
void sumOfKNumber(int * a, int n, int sum)
{
if(n <= 0 || sum <= 0)
return;
if(k > 0)
{
if(sum == a[n-1])
{
for(int i = k-1; i >= 0; --i)
cout << res[i] << "+";
cout << a[n-1] << endl; //特别注意,输出时,该元素还未加入数组
}
}
//考虑是否取第n个数
res[k++] = a[n-1];
sumOfKNumber(a, n-1, sum-a[n-1]);
k--;
sumOfKNumber(a, n-1, sum);
}
int main()
{
ifstream cin("in.txt");
int n, a[100], ans;
while(cin >> n)
{
for(int i = 0; i < n; ++i)
cin >> a[i];
cin >> ans;
sumOfKNumber(a, n, ans);
}
return 0;
}
三、k个和为定值的个数
题目:给出[1,2,3,4],k=2, target=5,[1,4] and [2,3]是2个符合要求的方案
地址:http://www.lintcode.com/zh-cn/problem/k-sum/
解析:dp[i][j][p] 表示从i个数中挑j个数和为p时的次数。
dp[0][0][0]表示在一个空集中找出0个数,target为0,则有1个解,就是什么也不挑嘛!
其实应该这样写,也就是说,找0个数,目标为0,则一定是有1个解:
if (j == 0 && p == 0) {
// select 0 number from i to the target: 0
D[i][j][p] = 1;
}
- 状态表达式:
D[i][j][p] = D[i - 1][j][p]; //不包含第i个元素
if (p - A[i - 1] >= 0) { //可以包含第i个元素
D[i][j][p] += D[i - 1][j - 1][t - A[i - 1]];
}
意思就是:
(1)我们可以把当前A[i - 1]这个值包括进来,所以需要加上D[i - 1][j - 1][t - A[i - 1]](前提是t - A[i - 1]要大于0)
(2)我们可以不选择A[i - 1]这个值,这种情况就是D[i - 1][j][t],也就是说直接在前i-1个值里选择一些值加到target.
public class Solution {
/**
* @param A: an integer array.
* @param k: a positive integer (k <= length(A))
* @param target: a integer
* @return an integer
*/
public int kSum(int A[], int k, int target) {
// write your code here
int len = A.length;
int [][][] dp = new int[len+1][k+1][target+1];
if(target < 0)
return 0;
for(int i = 0; i <= len; ++i)
for(int j = 0; j <= k; ++j)
for(int p = 0; p <= target; ++p)
{
if(j == 0 && p == 0)
dp[i][j][p] = 1;
else if(i != 0 && j != 0 && p!= 0)
{
dp[i][j][p] = dp[i-1][j][p];
if(p >= A[i-1])
dp[i][j][p] += dp[i-1][j-1][p-A[i-1]];
}
}
return dp[len][k][target];
}
}