一、尋找和為定值的兩個數
算法思想:
- 排序,然后兩端掃描。時間復雜度O(nlogn+n) 空間復雜度O(1)
- 散列映射,先確定其中一個數,再判斷另一個是否存在。時間復雜度O(1),空間復雜度O(n)
算法實現
#include <iostream>
#include <algorithm>
#include <fstream>
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 <iostream>
#include <fstream>
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];
}
}