algorithm - 通过分配符号来检查一系列整数是否为 0 的最有效算法是什么?
问题描述
我想以有效的方式解决以下问题:
给定一个整数序列,为每个整数分配一个符号(+ 或 -),使总和等于 0。对于所有序列,保证它们可以加到 0。
例子:
原始序列:1、3、5、2、1、4
输出:+5、-4、-3、+2、-1、+1
想法:
一个接一个地尝试每种组合。对于看起来像这样的 6 个数字(只是符号):
++++++
+++++-
++++-+
++++--
and so on...
尝试先对序列进行排序。将 + 分配给第一个数字,然后减去直到你是负数,然后再次添加直到你是正数。
first sort:
5, 4, 3, 2, 1, 1
+5 (sum = 5)
+5, -4 (sum = 1)
+5, -4, -3 (sum = -2)
+5, -4, -3, +2 (sum = 0)
+5, -4, -3, +2, -1 (sum = -1)
+5, -4, -3, +2, -1, +1 (sum = 0)
有没有更好的方法来解决这个问题?第二个是否有意义,或者是否有可能这不起作用(在您可以将 seq 添加到 0 的前提下)?
解决方案
如果您的第一个想法是:
您的第一个想法是一个接一个地尝试每种可能的组合并检查总和肯定会奏效,但问题是复杂性会非常高。为此,我们可以简单地这样做:
bool recursion(int pos, int n, int sum, vector<int>&sequence) {
if (pos == n) {
if (sum == 0) return true;
else return false;
}
bool resultTakingPositive = recursion(pos + 1, n, sum + sequence[pos], sequence);
bool resultTakingNegative = recursion(pos + 1, n, sum - sequence[pos], sequence);
if (resultTakingPositive || resultTakingNegative) return true;
else return false;
}
如果有总n
整数,那么这个解决方案的时间复杂度为O(2^n)
. 因为在每个位置,都有两种选择:
- 取 +ve 的总和值。
- 求和 -ve 值。
而且,我们必须为每个n
整数做出选择。因此,n
乘以2
会导致 O(2^n) 时间复杂度。
如果您有第二个想法:
您尝试首先以非递增顺序对序列进行排序并将 +ve 符号分配给第一个数字,然后减去直到得到负数,然后再次添加直到得到正数。不幸的是,这种贪婪的方法并不总是奏效。例如:
在一个序列中:5, 4, 4, 3, 2
如果我们尝试这种方法,我们将得到 :+5 -4 -4 +3 +2
这导致 summation = 2。
但是,我们可以通过执行 : 使总和为零+5 +4 -4 -3 -2
。
有效的方法:
我们可以在上述递归解决方案中通过简单的修改使用记忆化,以便在状态为pos
和的记忆化时允许正索引sum
。这也称为动态规划。为此, 的最高可能值pos * sum
应该更小,以便使用二维数组将它们的状态缓存在内存中。因此,时间和空间复杂度都将是O(n * sum)
。使用 c++ 代码的这种方法的示例将是:
#include<bits/stdc++.h>
using namespace std;
bool recursion(int pos, int n, int sum, vector<int>&sequence,int &baseSum, vector< vector<int> >&dp) {
if (pos == n) {
if (sum == baseSum) return true;
else return false;
}
if (dp[pos][sum] != -1) return dp[pos][sum];
bool resultTakingPositive = recursion(pos + 1, n, sum + sequence[pos], sequence, baseSum, dp);
bool resultTakingNegative = recursion(pos + 1, n, sum - sequence[pos], sequence, baseSum, dp);
dp[pos][sum] = (resultTakingPositive || resultTakingNegative);
return dp[pos][sum];
}
int main() {
vector<int>sequence;
int n, baseSum = 0;
scanf("%d",&n);
for (int i = 1; i <= n; i++) {
int x;
scanf("%d",&x);
sequence.push_back(x);
baseSum += x;
}
vector< vector<int> >dp(n, vector<int>(2*baseSum + 1, -1));
cout<<recursion(0, n, baseSum, sequence, baseSum, dp)<<endl;
return 0;
}
现在,如果我们想跟踪用于构成总和 0 的符号,我们可以通过分析递归调用来做到这一点,如下所示:
#include<bits/stdc++.h>
using namespace std;
bool recursion(int pos, int n, int sum, vector<int>&sequence,int &baseSum, vector< vector<int> >&dp) {
if (pos == n) {
if (sum == baseSum) return true;
else return false;
}
if (dp[pos][sum] != -1) return dp[pos][sum];
bool resultTakingPositive = recursion(pos + 1, n, sum + sequence[pos], sequence, baseSum, dp);
bool resultTakingNegative = recursion(pos + 1, n, sum - sequence[pos], sequence, baseSum, dp);
dp[pos][sum] = (resultTakingPositive || resultTakingNegative);
return dp[pos][sum];
}
void printSolution(int pos, int n, int sum, vector<int>&sequence,int &baseSum, vector< vector<int> >&dp) {
if (pos == n) {
cout<<endl;
return;
}
bool resultTakingPositive = recursion(pos + 1, n, sum + sequence[pos], sequence, baseSum, dp);
if (resultTakingPositive == true) {
cout<< "+ ";
printSolution(pos + 1, n, sum + sequence[pos], sequence, baseSum, dp);
} else {
cout<< "- ";
printSolution(pos + 1, n, sum - sequence[pos], sequence, baseSum, dp);
}
}
int main() {
vector<int>sequence;
int n, baseSum = 0;
scanf("%d",&n);
for (int i = 1; i <= n; i++) {
int x;
scanf("%d",&x);
sequence.push_back(x);
baseSum += x;
}
vector< vector<int> >dp(n, vector<int>(2*baseSum + 1, -1));
if (recursion(0, n, baseSum, sequence, baseSum, dp)) { // if possible to make sum 0 then
printSolution(0, n, baseSum, sequence, baseSum, dp);
}
return 0;
}
推荐阅读
- ruby-on-rails - Rails 6:ActionText 破坏了我的链接,如何禁用 URL 编码?
- r - 计算从下午 5 点到次日下午 5 点的每日平均值
- laravel - Laravel Lighthouse GraphQL:即使令牌存在请求标头,护照身份验证也不起作用
- java - 在 Spring 中未返回具有非标准 getter 名称的变量
- c# - 有没有办法在不触及子目录和文件的情况下获得 FTP 目录(文件夹)的大小?
- android - 有没有办法通过自动向上滑动来查看从终端窗口消失的android studio终端日志?
- c# - 在 ChangeView() 之后获取更新的 ScrollViewer 的 VerticalOffset
- python - 理解 asyncio.as_completed()
- angular - 如何根据资产文件夹中的js文件内容配置JwtModule.forRoot
- flask - 无法在我的 Flask 应用程序中导入和使用 JavaScript 文件