给定硬币的所有可能总和
All possible sum from a given of coins
您有 n 个具有特定价值的硬币。你的任务是找到你可以用这些硬币创造的所有钱数。
输入
第一行输入一个整数n:硬币的数量。
下一行有n个整数x1,x2,…,xn:硬币的价值。
输出
首先打印一个整数k:不同金额的数量。在此之后,按递增顺序打印所有可能的总和。
约束条件
1≤n≤100
1≤xi≤1000
例子
输入:
4
4 2 5 2
输出:
9
2 4 5 6 7 8 9 11 13
我写了一个代码,它对小输入非常有效,但对大输入给出了错误的答案。请帮忙找出错误,我该如何改正。
我的代码是:
#include <bits/stdc++.h>
using namespace std;
set<long long> s;
// Prints sums of all subsets of array
void subsetSums(long long arr[], long long n)
{
// There are totoal 2^n subsets
long long total = 1 << n;
// Consider all numbers from 0 to 2^n - 1
for (long long i = 0; i < total; i++)
{
long long sum = 0;
// Consider binary reprsentation of
// current i to decide which elements
// to pick.
for (long long j = 0; j < n; j++)
if (i & (1 << j))
sum += arr[j];
// Print sum of picked elements.
if (sum)
s.insert(sum);
}
}
// Driver code
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
long long n;
cin >> n;
long long arr[n];
for (long long i = 0; i < n; i++)
{
cin >> arr[i];
}
subsetSums(arr, n);
cout << s.size() << "\n";
for (auto it = s.begin(); it != s.end(); ++it)
cout << *it << " ";
return 0;
}
例如,它给出了
的错误答案
50
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
作为
18
2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36
正确的输出应该是:
50
1 2 3 4 ... 50
你的代码太慢了 2^n
子集在最坏的情况下(当 n=100
时)给出了 1,267,650,600,228,229,401,496,703,205,376
个子集,而 C++ 平均每秒执行 1000,000,000
次操作.
这个问题可以用动态规划来解决,考虑有一个大小为 100001
的数组 dp
,这样 dp[x]
表示如果 x
的和是可能的实现。
基本情况很简单 - 0
的总和可以不使用任何硬币:dp[0]=1
然后对于每个硬币,我们可以尝试通过硬币值增加现有总和来填充我们的 table:
for each coinValue:
for coinSum = 100000 - coinValue; coinSum >=0; coinSum--)
if(dp[coinSum])
dp[coinSum + coinValue]=1
请注意,我们正在向后循环,这是故意这样做的,以便每个硬币只使用一次。
复杂度:O(n^2*maxCoinValue)
你的算法很差,但你得到错误结果的原因是你溢出了 int
。 long long total = 1<<n;
将 int
左移 n
个位置,而您将结果分配给 long long
的事实无关紧要。
您可以使用 ubsan 找到此类问题。这是您的问题的重现,包括来自 ubsan 的警告消息:
$ clang++ -fsanitize=undefined a.cpp -o a && ./a
50
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
a.cpp:11:25: runtime error: shift exponent 50 is too large for 32-bit type 'int'
a.cpp:22:24: runtime error: shift exponent 32 is too large for 32-bit type 'int'
18
2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36
您有 n 个具有特定价值的硬币。你的任务是找到你可以用这些硬币创造的所有钱数。
输入
第一行输入一个整数n:硬币的数量。
下一行有n个整数x1,x2,…,xn:硬币的价值。
输出
首先打印一个整数k:不同金额的数量。在此之后,按递增顺序打印所有可能的总和。
约束条件
1≤n≤100
1≤xi≤1000
例子
输入:
4
4 2 5 2
输出:
9
2 4 5 6 7 8 9 11 13
我写了一个代码,它对小输入非常有效,但对大输入给出了错误的答案。请帮忙找出错误,我该如何改正。
我的代码是:
#include <bits/stdc++.h>
using namespace std;
set<long long> s;
// Prints sums of all subsets of array
void subsetSums(long long arr[], long long n)
{
// There are totoal 2^n subsets
long long total = 1 << n;
// Consider all numbers from 0 to 2^n - 1
for (long long i = 0; i < total; i++)
{
long long sum = 0;
// Consider binary reprsentation of
// current i to decide which elements
// to pick.
for (long long j = 0; j < n; j++)
if (i & (1 << j))
sum += arr[j];
// Print sum of picked elements.
if (sum)
s.insert(sum);
}
}
// Driver code
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
long long n;
cin >> n;
long long arr[n];
for (long long i = 0; i < n; i++)
{
cin >> arr[i];
}
subsetSums(arr, n);
cout << s.size() << "\n";
for (auto it = s.begin(); it != s.end(); ++it)
cout << *it << " ";
return 0;
}
例如,它给出了
的错误答案50
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
作为
18
2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36
正确的输出应该是:
50
1 2 3 4 ... 50
你的代码太慢了 2^n
子集在最坏的情况下(当 n=100
时)给出了 1,267,650,600,228,229,401,496,703,205,376
个子集,而 C++ 平均每秒执行 1000,000,000
次操作.
这个问题可以用动态规划来解决,考虑有一个大小为 100001
的数组 dp
,这样 dp[x]
表示如果 x
的和是可能的实现。
基本情况很简单 - 0
的总和可以不使用任何硬币:dp[0]=1
然后对于每个硬币,我们可以尝试通过硬币值增加现有总和来填充我们的 table:
for each coinValue:
for coinSum = 100000 - coinValue; coinSum >=0; coinSum--)
if(dp[coinSum])
dp[coinSum + coinValue]=1
请注意,我们正在向后循环,这是故意这样做的,以便每个硬币只使用一次。
复杂度:O(n^2*maxCoinValue)
你的算法很差,但你得到错误结果的原因是你溢出了 int
。 long long total = 1<<n;
将 int
左移 n
个位置,而您将结果分配给 long long
的事实无关紧要。
您可以使用 ubsan 找到此类问题。这是您的问题的重现,包括来自 ubsan 的警告消息:
$ clang++ -fsanitize=undefined a.cpp -o a && ./a
50
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
a.cpp:11:25: runtime error: shift exponent 50 is too large for 32-bit type 'int'
a.cpp:22:24: runtime error: shift exponent 32 is too large for 32-bit type 'int'
18
2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36