从给定的数组(称之为 numbers [] ),我想要另一个数组(结果[] ),其中包含第一个数组元素之间的所有和的可能性。
例如,如果我有数字[] = {1,3,5},则结果[]将为{1,3,5,4,8,6,9,0}。 有2 ^ n种可能性。 如果数字出现两次并不重要,因为结果[]将是set
我做了对或三联的总和,这很容易。但是当我们总结0,1,2或n个数字时,我不明白它是如何工作的。
这就是我对配对做的事情:
std::unordered_set<int> pairPossibilities(std::vector<int> &numbers) {
std::unordered_set<int> results;
for(int i=0;i<numbers.size()-1;i++) {
for(int j=i+1;j<numbers.size();j++) {
results.insert(numbers.at(i)+numbers.at(j));
}
}
return results;
}
另外,假设数字[]已排序,是否有可能在填写结果时对结果[]进行排序?
谢谢!
答案 0 :(得分:2)
这可以通过O(n*W)
W = sum{numbers}
DP[i, 0] = true
DP[-1, w] = false w != 0
DP[i, w] = DP[i-1, w] OR DP[i-1, w - numbers[i]]
中的Dynamic Programming (DP)来完成。
这与Subset Sum Problem的解决方案基本相同,利用了问题具有最优子结构的事实。
DP[n, sum{numbers}]
首先按照上述解决方案查找DP[n , w] = true
。
结果,你会得到:
w
当且仅当numbers
可以构建 //MessageReceiver.java
public class MessageReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
SmsMessage[] messages;
String str = "";
if (bundle != null) {
Object[] pdus = (Object[]) bundle.get("pdus");
messages = new SmsMessage[pdus != null ? pdus.length : 0];
for (int i = 0; i < messages.length; i++) {
messages[i] = SmsMessage.createFromPdu((byte[]) (pdus != null ? pdus[i] : null));
str += messages[i].getOriginatingAddress();
str += ":";
str += messages[i].getMessageBody();
str += "\n";
}
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("SMS_RECIEVED_ACTION");
broadcastIntent.putExtra("message", str);
context.sendBroadcast(broadcastIntent);}}}
答案 1 :(得分:1)
我会做这样的事情(看起来更容易)[我想把它放在评论中,但不能一次写下转移和删除一个元素 - 你可能需要一个链表]
1 3 5
3 5
-----
4 8
1 3 5
5
-----
6
1 3 5
3 5
5
------
9
最后将0
添加到列表中。
解决此问题的另一种方法是创建元素向量的子集数组,然后对每个数组的向量数据求和。
例如
删除单个元素集后1 3 5 = {1, 3} + {1,5} + {3,5} + {1,3,5}
。
请记住,说起来容易做起来难。实现算法中的一个小错误在调试中需要花费大量时间才能找到它。 =]]
答案 2 :(得分:1)
继动态编程答案后,您可以使用递归解决方案,然后使用memoization缓存结果,自上而下的方法与Amit的自下而上相比。
vector<int> subsetSum(vector<int>& nums)
{
vector<int> ans;
generateSubsetSum(ans,0,nums,0);
return ans;
}
void generateSubsetSum(vector<int>& ans, int sum, vector<int>& nums, int i)
{
if(i == nums.size() )
{
ans.push_back(sum);
return;
}
generateSubsetSum(ans,sum + nums[i],nums,i + 1);
generateSubsetSum(ans,sum,nums,i + 1);
}
集合Result is : {9 4 6 1 8 3 5 0}
的 {1,3,5}
这只是选择第一个索引处的第一个数字i
将其添加到sum
并进行递归。返回后,第二个分支跟随sum
,而不添加nums[i]
。要记住这一点,您可以使用缓存将sum
存储在i
。
答案 3 :(得分:1)
还必须有二进制版本。这个有点笨拙,依赖于你提到的一组答案来过滤重复的结果:
Split the list into 2,
and generate the list of sums for each half
by recursion:
the minimum state is either
2 entries, with 1 result,
or 3 entries with 3 results
alternatively, take it down to 1 entry with 0 results, if you insist
Then combine the 2 halves:
All the returned entries from both halves are legitimate results
There are 4 additional result sets to add to the output result by combining:
The first half inputs vs the second half inputs
The first half outputs vs the second half inputs
The first half inputs vs the second half outputs
The first half outputs vs the second half outputs
请注意,两半的输出可能有一些共同的元素,但对于这些组合应分别对它们进行处理。
如果输入是合法的最终结果,则可以从每次递归的返回输出中清除输入。如果它们可以在顶级阶段重新加入,或者在底层阶段返回,而不是在合并中再次考虑。
您可以使用位域而不是集来过滤掉重复项。有一些合理有效的方法来逐步通过位域来查找所有设置位。位域的最大大小是所有输入的总和。
这里没有智能,但在递归和并行步骤中并行处理的机会很多。